1717import subprocess
1818import sys
1919import threading
20+ from textwrap import dedent
2021
2122from git .compat import (
2223 string_types ,
3132)
3233from git .exc import CommandError
3334from git .odict import OrderedDict
34- from git .util import is_cygwin_git , cygpath
35+ from git .util import is_cygwin_git , cygpath , expand_path
3536
3637from .exc import (
3738 GitCommandError ,
4647execute_kwargs = set (('istream' , 'with_extended_output' ,
4748 'with_exceptions' , 'as_process' , 'stdout_as_string' ,
4849 'output_stream' , 'with_stdout' , 'kill_after_timeout' ,
49- 'universal_newlines' , 'shell' ))
50+ 'universal_newlines' , 'shell' , 'env' ))
5051
5152log = logging .getLogger (__name__ )
5253log .addHandler (logging .NullHandler ())
@@ -182,16 +183,141 @@ def __setstate__(self, d):
182183 # Enables debugging of GitPython's git commands
183184 GIT_PYTHON_TRACE = os .environ .get ("GIT_PYTHON_TRACE" , False )
184185
185- # Provide the full path to the git executable. Otherwise it assumes git is in the path
186- _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE"
187- GIT_PYTHON_GIT_EXECUTABLE = os .environ .get (_git_exec_env_var , git_exec_name )
188-
189186 # If True, a shell will be used when executing git commands.
190187 # This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126
191188 # and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required.
192189 # Override this value using `Git.USE_SHELL = True`
193190 USE_SHELL = False
194191
192+ # Provide the full path to the git executable. Otherwise it assumes git is in the path
193+ _git_exec_env_var = "GIT_PYTHON_GIT_EXECUTABLE"
194+ _refresh_env_var = "GIT_PYTHON_REFRESH"
195+ GIT_PYTHON_GIT_EXECUTABLE = None
196+ # note that the git executable is actually found during the refresh step in
197+ # the top level __init__
198+
199+ @classmethod
200+ def refresh (cls , path = None ):
201+ """This gets called by the refresh function (see the top level
202+ __init__).
203+ """
204+ # discern which path to refresh with
205+ if path is not None :
206+ new_git = os .path .expanduser (path )
207+ new_git = os .path .abspath (new_git )
208+ else :
209+ new_git = os .environ .get (cls ._git_exec_env_var , cls .git_exec_name )
210+
211+ # keep track of the old and new git executable path
212+ old_git = cls .GIT_PYTHON_GIT_EXECUTABLE
213+ cls .GIT_PYTHON_GIT_EXECUTABLE = new_git
214+
215+ # test if the new git executable path is valid
216+
217+ if sys .version_info < (3 ,):
218+ # - a GitCommandNotFound error is spawned by ourselves
219+ # - a OSError is spawned if the git executable provided
220+ # cannot be executed for whatever reason
221+ exceptions = (GitCommandNotFound , OSError )
222+ else :
223+ # - a GitCommandNotFound error is spawned by ourselves
224+ # - a PermissionError is spawned if the git executable provided
225+ # cannot be executed for whatever reason
226+ exceptions = (GitCommandNotFound , PermissionError )
227+
228+ has_git = False
229+ try :
230+ cls ().version ()
231+ has_git = True
232+ except exceptions :
233+ pass
234+
235+ # warn or raise exception if test failed
236+ if not has_git :
237+ err = dedent ("""\
238+ Bad git executable.
239+ The git executable must be specified in one of the following ways:
240+ - be included in your $PATH
241+ - be set via $%s
242+ - explicitly set via git.refresh()
243+ """ ) % cls ._git_exec_env_var
244+
245+ # revert to whatever the old_git was
246+ cls .GIT_PYTHON_GIT_EXECUTABLE = old_git
247+
248+ if old_git is None :
249+ # on the first refresh (when GIT_PYTHON_GIT_EXECUTABLE is
250+ # None) we only are quiet, warn, or error depending on the
251+ # GIT_PYTHON_REFRESH value
252+
253+ # determine what the user wants to happen during the initial
254+ # refresh we expect GIT_PYTHON_REFRESH to either be unset or
255+ # be one of the following values:
256+ # 0|q|quiet|s|silence
257+ # 1|w|warn|warning
258+ # 2|r|raise|e|error
259+
260+ mode = os .environ .get (cls ._refresh_env_var , "raise" ).lower ()
261+
262+ quiet = ["quiet" , "q" , "silence" , "s" , "none" , "n" , "0" ]
263+ warn = ["warn" , "w" , "warning" , "1" ]
264+ error = ["error" , "e" , "raise" , "r" , "2" ]
265+
266+ if mode in quiet :
267+ pass
268+ elif mode in warn or mode in error :
269+ err = dedent ("""\
270+ %s
271+ All git commands will error until this is rectified.
272+
273+ This initial warning can be silenced or aggravated in the future by setting the
274+ $%s environment variable. Use one of the following values:
275+ - %s: for no warning or exception
276+ - %s: for a printed warning
277+ - %s: for a raised exception
278+
279+ Example:
280+ export %s=%s
281+ """ ) % (
282+ err ,
283+ cls ._refresh_env_var ,
284+ "|" .join (quiet ),
285+ "|" .join (warn ),
286+ "|" .join (error ),
287+ cls ._refresh_env_var ,
288+ quiet [0 ])
289+
290+ if mode in warn :
291+ print ("WARNING: %s" % err )
292+ else :
293+ raise ImportError (err )
294+ else :
295+ err = dedent ("""\
296+ %s environment variable has been set but it has been set with an invalid value.
297+
298+ Use only the following values:
299+ - %s: for no warning or exception
300+ - %s: for a printed warning
301+ - %s: for a raised exception
302+ """ ) % (
303+ cls ._refresh_env_var ,
304+ "|" .join (quiet ),
305+ "|" .join (warn ),
306+ "|" .join (error ))
307+ raise ImportError (err )
308+
309+ # we get here if this was the init refresh and the refresh mode
310+ # was not error, go ahead and set the GIT_PYTHON_GIT_EXECUTABLE
311+ # such that we discern the difference between a first import
312+ # and a second import
313+ cls .GIT_PYTHON_GIT_EXECUTABLE = cls .git_exec_name
314+ else :
315+ # after the first refresh (when GIT_PYTHON_GIT_EXECUTABLE
316+ # is no longer None) we raise an exception
317+ raise GitCommandNotFound ("git" , err )
318+
319+ return has_git
320+
195321 @classmethod
196322 def is_cygwin (cls ):
197323 return is_cygwin_git (cls .GIT_PYTHON_GIT_EXECUTABLE )
@@ -405,7 +531,7 @@ def __init__(self, working_dir=None):
405531 It is meant to be the working tree directory if available, or the
406532 .git directory in case of bare repositories."""
407533 super (Git , self ).__init__ ()
408- self ._working_dir = working_dir
534+ self ._working_dir = expand_path ( working_dir )
409535 self ._git_options = ()
410536 self ._persistent_git_options = []
411537
@@ -471,6 +597,7 @@ def execute(self, command,
471597 with_stdout = True ,
472598 universal_newlines = False ,
473599 shell = None ,
600+ env = None ,
474601 ** subprocess_kwargs
475602 ):
476603 """Handles executing the command on the shell and consumes and returns
@@ -514,6 +641,9 @@ def execute(self, command,
514641 decoded into a string using the default encoding (usually utf-8).
515642 The latter can fail, if the output contains binary data.
516643
644+ :param env:
645+ A dictionary of environment variables to be passed to `subprocess.Popen`.
646+
517647 :param subprocess_kwargs:
518648 Keyword arguments to be passed to subprocess.Popen. Please note that
519649 some of the valid kwargs are already set by this method, the ones you
@@ -559,6 +689,7 @@ def execute(self, command,
559689 cwd = self ._working_dir or os .getcwd ()
560690
561691 # Start the process
692+ inline_env = env
562693 env = os .environ .copy ()
563694 # Attempt to force all output to plain ascii english, which is what some parsing code
564695 # may expect.
@@ -567,6 +698,8 @@ def execute(self, command,
567698 env ["LANGUAGE" ] = "C"
568699 env ["LC_ALL" ] = "C"
569700 env .update (self ._environment )
701+ if inline_env is not None :
702+ env .update (inline_env )
570703
571704 if is_win :
572705 cmd_not_found_exception = OSError
@@ -828,13 +961,13 @@ def _call_process(self, method, *args, **kwargs):
828961 - "command options" to be converted by :meth:`transform_kwargs()`;
829962 - the `'insert_kwargs_after'` key which its value must match one of ``*args``,
830963 and any cmd-options will be appended after the matched arg.
831-
964+
832965 Examples::
833-
966+
834967 git.rev_list('master', max_count=10, header=True)
835-
968+
836969 turns into::
837-
970+
838971 git rev-list max-count 10 --header master
839972
840973 :return: Same as ``execute``"""
0 commit comments