difftool launchers: better behavior when amending commits
[git-cola.git] / cola / git.py
blob3893f2ee58e6eeca372ccaf107dc68e6647834d9
1 # cmd.py
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
7 import os
8 import sys
9 import subprocess
11 def dashify(string):
12 return string.replace('_', '-')
14 # Enables debugging of GitPython's git commands
15 GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
17 execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
18 'with_exceptions', 'with_raw_output')
20 extra = {}
21 if sys.platform == 'win32':
22 extra = {'shell': True}
24 class Git(object):
25 """
26 The Git class manages communication with the Git binary
27 """
28 def __init__(self, git_dir):
29 super(Git, self).__init__()
30 self.git_dir = git_dir
32 def __getattr__(self, name):
33 if name[:1] == '_':
34 raise AttributeError(name)
35 return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)
37 @property
38 def get_dir(self):
39 return self.git_dir
41 def execute(self, command,
42 istream=None,
43 with_keep_cwd=False,
44 with_extended_output=False,
45 with_exceptions=True,
46 with_raw_output=False,
48 """
49 Handles executing the command on the shell and consumes and returns
50 the returned information (stdout)
52 ``command``
53 The command argument list to execute
55 ``istream``
56 Standard input filehandle passed to subprocess.Popen.
58 ``with_keep_cwd``
59 Whether to use the current working directory from os.getcwd().
60 GitPython uses get_work_tree() as its working directory by
61 default and get_git_dir() for bare repositories.
63 ``with_extended_output``
64 Whether to return a (status, stdout, stderr) tuple.
66 ``with_exceptions``
67 Whether to raise an exception when git returns a non-zero status.
69 ``with_raw_output``
70 Whether to avoid stripping off trailing whitespace.
72 Returns
73 str(output) # extended_output = False (Default)
74 tuple(int(status), str(output)) # extended_output = True
75 """
77 if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full':
78 print ' '.join(command)
80 # Allow the user to have the command executed in their working dir.
81 if with_keep_cwd or self.git_dir is None:
82 cwd = os.getcwd()
83 else:
84 cwd=self.git_dir
86 # Start the process
87 proc = subprocess.Popen(command,
88 cwd=cwd,
89 stdin=istream,
90 stderr=subprocess.PIPE,
91 stdout=subprocess.PIPE,
92 **extra)
94 # Wait for the process to return
95 try:
96 stdout_value = proc.stdout.read().decode('utf-8')
97 stderr_value = proc.stderr.read().decode('utf-8')
98 status = proc.wait()
99 proc.stdout.close()
100 proc.stderr.close()
101 except:
102 status = 255
103 stdout_value = ''
104 stderr_value = ''
106 # Strip off trailing whitespace by default
107 if not with_raw_output:
108 stdout_value = stdout_value.rstrip()
109 stderr_value = stderr_value.rstrip()
111 if with_exceptions and status != 0:
112 raise GitCommandError(command, status, stderr_value)
114 if GIT_PYTHON_TRACE == 'full':
115 if stderr_value:
116 print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value)
117 elif stdout_value:
118 print "%s -> %d: '%s'" % (command, status, stdout_value)
119 else:
120 print "%s -> %d" % (command, status)
122 # Allow access to the command's status code
123 if with_extended_output:
124 return (status, stdout_value, stderr_value)
125 else:
126 return stdout_value
128 def transform_kwargs(self, **kwargs):
130 Transforms Python style kwargs into git command line options.
132 args = []
133 for k, v in kwargs.items():
134 if len(k) == 1:
135 if v is True:
136 args.append("-%s" % k)
137 elif type(v) is not bool:
138 args.append("-%s%s" % (k, v))
139 else:
140 if v is True:
141 args.append("--%s" % dashify(k))
142 elif type(v) is not bool:
143 args.append("--%s=%s" % (dashify(k), v))
144 return args
146 def _call_process(self, method, *args, **kwargs):
148 Run the given git command with the specified arguments and return
149 the result as a String
151 ``method``
152 is the command
154 ``args``
155 is the list of arguments
157 ``kwargs``
158 is a dict of keyword arguments.
159 This function accepts the same optional keyword arguments
160 as execute().
162 Examples
163 git.rev_list('master', max_count=10, header=True)
165 Returns
166 Same as execute()
169 # Handle optional arguments prior to calling transform_kwargs
170 # otherwise these'll end up in args, which is bad.
171 _kwargs = {}
172 for kwarg in execute_kwargs:
173 try:
174 _kwargs[kwarg] = kwargs.pop(kwarg)
175 except KeyError:
176 pass
178 # Prepare the argument list
179 opt_args = self.transform_kwargs(**kwargs)
180 ext_args = [a.encode('utf-8') for a in args]
181 args = opt_args + ext_args
183 call = ["git", dashify(method)]
184 call.extend(args)
186 return self.execute(call, **_kwargs)