2 # Copyright (C) 2008 Michael Trier (mtrier@gmail.com) and contributors
4 # This module is part of GitPython and is released under
5 # the BSD License: http://www.opensource.org/licenses/bsd-license.php
10 from cola
.exception
import GitCommandError
13 return string
.replace('_', '-')
15 # Enables debugging of GitPython's git commands
16 GIT_PYTHON_TRACE
= os
.environ
.get("GIT_PYTHON_TRACE", False)
18 execute_kwargs
= ('istream', 'with_keep_cwd', 'with_extended_output',
19 'with_exceptions', 'with_raw_output')
23 The Git class manages communication with the Git binary
28 def set_cwd(self
, path
):
31 def __getattr__(self
, name
):
33 raise AttributeError(name
)
34 return lambda *args
, **kwargs
: self
._call
_process
(name
, *args
, **kwargs
)
41 with_extended_output
=False,
43 with_raw_output
=False):
45 Handles executing the command on the shell and consumes and returns
46 the returned information (stdout)
49 The command argument list to execute
52 Standard input filehandle passed to subprocess.Popen.
55 Whether to use the current working directory from os.getcwd().
56 GitPython uses the cwd set by set_cwd() by default.
58 ``with_extended_output``
59 Whether to return a (status, stdout, stderr) tuple.
62 Whether to raise an exception when git returns a non-zero status.
65 Whether to avoid stripping off trailing whitespace.
68 str(output) # extended_output = False (Default)
69 tuple(int(status), str(output)) # extended_output = True
72 if GIT_PYTHON_TRACE
and not GIT_PYTHON_TRACE
== 'full':
73 print ' '.join(command
)
75 # Allow the user to have the command executed in their working dir.
76 if with_keep_cwd
or not cwd
:
80 wanky
= sys
.platform
in ('win32',)
82 command
= shell_quote(*command
)
84 proc
= subprocess
.Popen(command
,
88 stderr
=subprocess
.PIPE
,
89 stdout
=subprocess
.PIPE
)
93 while count
< 4: # osx interrupts system calls
96 stdout_value
, stderr_value
= proc
.communicate()
100 status
= proc
.returncode
101 if with_exceptions
and status
:
102 raise GitCommandError(command
, status
, stderr_value
)
108 if not with_raw_output
:
109 stdout_value
= stdout_value
.strip()
110 stderr_value
= stderr_value
.strip()
112 if GIT_PYTHON_TRACE
== 'full':
114 print "%s -> %d: '%s' !! '%s'" % (command
, status
, stdout_value
, stderr_value
)
116 print "%s -> %d: '%s'" % (command
, status
, stdout_value
)
118 print "%s -> %d" % (command
, status
)
120 # Allow access to the command's status code
121 if with_extended_output
:
122 return (status
, stdout_value
, stderr_value
)
124 if stdout_value
and stderr_value
:
125 return stderr_value
+ '\n' + stdout_value
131 def transform_kwargs(self
, **kwargs
):
133 Transforms Python style kwargs into git command line options.
136 for k
, v
in kwargs
.items():
139 args
.append("-%s" % k
)
140 elif type(v
) is not bool:
141 args
.append("-%s%s" % (k
, v
))
144 args
.append("--%s" % dashify(k
))
145 elif type(v
) is not bool:
146 args
.append("--%s=%s" % (dashify(k
), v
))
149 def _call_process(self
, method
, *args
, **kwargs
):
151 Run the given git command with the specified arguments and return
152 the result as a String
158 is the list of arguments
161 is a dict of keyword arguments.
162 This function accepts the same optional keyword arguments
166 git.rev_list('master', max_count=10, header=True)
172 # Handle optional arguments prior to calling transform_kwargs
173 # otherwise these'll end up in args, which is bad.
174 _kwargs
= dict(cwd
=self
._git
_cwd
)
175 for kwarg
in execute_kwargs
:
177 _kwargs
[kwarg
] = kwargs
.pop(kwarg
)
181 # Prepare the argument list
182 opt_args
= self
.transform_kwargs(**kwargs
)
183 ext_args
= [a
.encode('utf-8') for a
in args
]
184 args
= opt_args
+ ext_args
186 call
= ['git', dashify(method
)]
189 return Git
.execute(call
, **_kwargs
)
192 def shell_quote(*inputs
):
194 Quote strings so that they can be suitably martialled
195 off to the shell. This method supports POSIX sh syntax.
196 This is crucial to properly handle command line arguments
197 with spaces, quotes, double-quotes, etc. on darwin/win32...
200 regex
= re
.compile('[^\w!%+,\-./:@^]')
201 quote_regex
= re
.compile("((?:'\\''){2,})")
209 raise AssertionError,('No way to quote strings '
210 'containing null(\\000) bytes')
212 # = does need quoting else in command position it's a
213 # program-local environment setting
214 match
= regex
.search(input)
215 if match
and '=' not in input:
217 input = input.replace("'", "'\\''")
219 # make multiple ' in a row look simpler
220 # '\'''\'''\'' -> '"'''"'
221 quote_match
= quote_regex
.match(input)
223 quotes
= match
.group(1)
224 input.replace(quotes
, ("'" *(len(quotes
)/4)) + "\"'")
226 input = "'%s'" % input
227 if input.startswith("''"):
230 if input.endswith("''"):