Added license information to all files.
[jelmer/gitpython.git] / lib / git / cmd.py
1 # cmd.py
2 # Copyright (C) 2008 Michael Trier (mtrier@gmail.com) and contributors
3 #
4 # This module is part of GitPython and is released under
5 # the BSD License: http://www.opensource.org/licenses/bsd-license.php
6
7 import os
8 import subprocess
9 import re
10 from utils import *
11 from method_missing import MethodMissingMixin
12 from errors import GitCommandError
13
14 # Enables debugging of GitPython's git commands
15 GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)
16
17 execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
18                   'with_exceptions', 'with_raw_output')
19
20 class Git(MethodMissingMixin):
21     """
22     The Git class manages communication with the Git binary
23     """
24     def __init__(self, git_dir):
25         super(Git, self).__init__()
26         self.git_dir = git_dir
27
28     @property
29     def get_dir(self):
30         return self.git_dir
31
32     def execute(self, command,
33                 istream=None,
34                 with_keep_cwd=False,
35                 with_extended_output=False,
36                 with_exceptions=True,
37                 with_raw_output=False,
38                 ):
39         """
40         Handles executing the command on the shell and consumes and returns
41         the returned information (stdout)
42
43         ``command``
44             The command argument list to execute
45
46         ``istream``
47             Standard input filehandle passed to subprocess.Popen.
48
49         ``with_keep_cwd``
50             Whether to use the current working directory from os.getcwd().
51             GitPython uses get_work_tree() as its working directory by
52             default and get_git_dir() for bare repositories.
53
54         ``with_extended_output``
55             Whether to return a (status, stdout, stderr) tuple.
56
57         ``with_exceptions``
58             Whether to raise an exception when git returns a non-zero status.
59
60         ``with_raw_output``
61             Whether to avoid stripping off trailing whitespace.
62
63         Returns
64             str(output)                     # extended_output = False (Default)
65             tuple(int(status), str(output)) # extended_output = True
66         """
67
68         if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full':
69             print ' '.join(command)
70
71         # Allow the user to have the command executed in their working dir.
72         if with_keep_cwd or self.git_dir is None:
73           cwd = os.getcwd()
74         else:
75           cwd=self.git_dir
76
77         # Start the process
78         proc = subprocess.Popen(command,
79                                 cwd=cwd,
80                                 stdin=istream,
81                                 stderr=subprocess.PIPE,
82                                 stdout=subprocess.PIPE
83                                 )
84
85         # Wait for the process to return
86         try:
87             stdout_value = proc.stdout.read()
88             stderr_value = proc.stderr.read()
89             status = proc.wait()
90         finally:
91             proc.stdout.close()
92             proc.stderr.close()
93
94         # Strip off trailing whitespace by default
95         if not with_raw_output:
96             stdout_value = stdout_value.rstrip()
97             stderr_value = stderr_value.rstrip()
98
99         if with_exceptions and status != 0:
100             raise GitCommandError(command, status, stderr_value)
101
102         if GIT_PYTHON_TRACE == 'full':
103             if stderr_value:
104               print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value)
105             elif stdout_value:
106               print "%s -> %d: '%s'" % (command, status, stdout_value)
107             else:
108               print "%s -> %d" % (command, status)
109
110         # Allow access to the command's status code
111         if with_extended_output:
112             return (status, stdout_value, stderr_value)
113         else:
114             return stdout_value
115
116     def transform_kwargs(self, **kwargs):
117         """
118         Transforms Python style kwargs into git command line options.
119         """
120         args = []
121         for k, v in kwargs.items():
122             if len(k) == 1:
123                 if v is True:
124                     args.append("-%s" % k)
125                 elif type(v) is not bool:
126                     args.append("-%s%s" % (k, v))
127             else:
128                 if v is True:
129                     args.append("--%s" % dashify(k))
130                 elif type(v) is not bool:
131                     args.append("--%s=%s" % (dashify(k), v))
132         return args
133
134     def method_missing(self, method, *args, **kwargs):
135         """
136         Run the given git command with the specified arguments and return
137         the result as a String
138
139         ``method``
140             is the command
141
142         ``args``
143             is the list of arguments
144
145         ``kwargs``
146             is a dict of keyword arguments.
147             This function accepts the same optional keyword arguments
148             as execute().
149
150         Examples
151             git.rev_list('master', max_count=10, header=True)
152
153         Returns
154             Same as execute()
155         """
156
157         # Handle optional arguments prior to calling transform_kwargs
158         # otherwise these'll end up in args, which is bad.
159         _kwargs = {}
160         for kwarg in execute_kwargs:
161             try:
162                 _kwargs[kwarg] = kwargs.pop(kwarg)
163             except KeyError:
164                 pass
165
166         # Prepare the argument list
167         opt_args = self.transform_kwargs(**kwargs)
168         ext_args = map(str, args)
169         args = opt_args + ext_args
170
171         call = ["git", dashify(method)]
172         call.extend(args)
173
174         return self.execute(call, **_kwargs)