17
17
import subprocess
18
18
import sys
19
19
import threading
20
+ from textwrap import dedent
20
21
21
22
from git .compat import (
22
23
string_types ,
31
32
)
32
33
from git .exc import CommandError
33
34
from git .odict import OrderedDict
34
- from git .util import is_cygwin_git , cygpath
35
+ from git .util import is_cygwin_git , cygpath , expand_path
35
36
36
37
from .exc import (
37
38
GitCommandError ,
46
47
execute_kwargs = set (('istream' , 'with_extended_output' ,
47
48
'with_exceptions' , 'as_process' , 'stdout_as_string' ,
48
49
'output_stream' , 'with_stdout' , 'kill_after_timeout' ,
49
- 'universal_newlines' , 'shell' ))
50
+ 'universal_newlines' , 'shell' , 'env' ))
50
51
51
52
log = logging .getLogger (__name__ )
52
53
log .addHandler (logging .NullHandler ())
@@ -182,16 +183,141 @@ def __setstate__(self, d):
182
183
# Enables debugging of GitPython's git commands
183
184
GIT_PYTHON_TRACE = os .environ .get ("GIT_PYTHON_TRACE" , False )
184
185
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
-
189
186
# If True, a shell will be used when executing git commands.
190
187
# This should only be desirable on Windows, see https://github.com/gitpython-developers/GitPython/pull/126
191
188
# and check `git/test_repo.py:TestRepo.test_untracked_files()` TC for an example where it is required.
192
189
# Override this value using `Git.USE_SHELL = True`
193
190
USE_SHELL = False
194
191
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
+
195
321
@classmethod
196
322
def is_cygwin (cls ):
197
323
return is_cygwin_git (cls .GIT_PYTHON_GIT_EXECUTABLE )
@@ -405,7 +531,7 @@ def __init__(self, working_dir=None):
405
531
It is meant to be the working tree directory if available, or the
406
532
.git directory in case of bare repositories."""
407
533
super (Git , self ).__init__ ()
408
- self ._working_dir = working_dir
534
+ self ._working_dir = expand_path ( working_dir )
409
535
self ._git_options = ()
410
536
self ._persistent_git_options = []
411
537
@@ -471,6 +597,7 @@ def execute(self, command,
471
597
with_stdout = True ,
472
598
universal_newlines = False ,
473
599
shell = None ,
600
+ env = None ,
474
601
** subprocess_kwargs
475
602
):
476
603
"""Handles executing the command on the shell and consumes and returns
@@ -514,6 +641,9 @@ def execute(self, command,
514
641
decoded into a string using the default encoding (usually utf-8).
515
642
The latter can fail, if the output contains binary data.
516
643
644
+ :param env:
645
+ A dictionary of environment variables to be passed to `subprocess.Popen`.
646
+
517
647
:param subprocess_kwargs:
518
648
Keyword arguments to be passed to subprocess.Popen. Please note that
519
649
some of the valid kwargs are already set by this method, the ones you
@@ -559,6 +689,7 @@ def execute(self, command,
559
689
cwd = self ._working_dir or os .getcwd ()
560
690
561
691
# Start the process
692
+ inline_env = env
562
693
env = os .environ .copy ()
563
694
# Attempt to force all output to plain ascii english, which is what some parsing code
564
695
# may expect.
@@ -567,6 +698,8 @@ def execute(self, command,
567
698
env ["LANGUAGE" ] = "C"
568
699
env ["LC_ALL" ] = "C"
569
700
env .update (self ._environment )
701
+ if inline_env is not None :
702
+ env .update (inline_env )
570
703
571
704
if is_win :
572
705
cmd_not_found_exception = OSError
@@ -647,8 +780,8 @@ def _kill_process(pid):
647
780
if kill_after_timeout :
648
781
watchdog .cancel ()
649
782
if kill_check .isSet ():
650
- stderr_value = 'Timeout: the command "%s" did not complete in %d ' \
651
- 'secs.' % (" " .join (command ), kill_after_timeout )
783
+ stderr_value = ( 'Timeout: the command "%s" did not complete in %d '
784
+ 'secs.' % (" " .join (command ), kill_after_timeout )). encode ( defenc )
652
785
# strip trailing "\n"
653
786
if stdout_value .endswith (b"\n " ):
654
787
stdout_value = stdout_value [:- 1 ]
@@ -828,13 +961,13 @@ def _call_process(self, method, *args, **kwargs):
828
961
- "command options" to be converted by :meth:`transform_kwargs()`;
829
962
- the `'insert_kwargs_after'` key which its value must match one of ``*args``,
830
963
and any cmd-options will be appended after the matched arg.
831
-
964
+
832
965
Examples::
833
-
966
+
834
967
git.rev_list('master', max_count=10, header=True)
835
-
968
+
836
969
turns into::
837
-
970
+
838
971
git rev-list max-count 10 --header master
839
972
840
973
:return: Same as ``execute``"""
0 commit comments