﻿# cmd.py
# Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
#
# This module is part of GitPython and is released under
# the BSD License: http://www.opensource.org/licenses/bsd-license.php

import os, sys
import subprocess
import re
from utils import *
from errors import GitCommandError

# Enables debugging of GitPython's git commands
GIT_PYTHON_TRACE = os.environ.get("GIT_PYTHON_TRACE", False)

execute_kwargs = ('istream', 'with_keep_cwd', 'with_extended_output',
                  'with_exceptions', 'with_raw_output')

extra = {}
if sys.platform == 'win32':
    extra = {'shell': True}

class Git(object):
    """
    The Git class manages communication with the Git binary.
    
	It provides a convenient interface to calling the Git binary, such as in::
	
	 g = Git( git_dir )
	 g.init()			        # calls 'git init' program
	 rval = g.ls_files()		# calls 'git ls-files' program
	
	``Debugging``
	    Set the GIT_PYTHON_TRACE environment variable print each invocation 
	    of the command to stdout.
	    Set its value to 'full' to see details about the returned values.
    """
    def __init__(self, git_dir=None):
        """
        Initialize this instance with:
        
        ``git_dir``
           Git directory we should work in. If None, we always work in the current 
           directory as returned by os.getcwd()
        """
        super(Git, self).__init__()
        self.git_dir = git_dir

    def __getattr__(self, name):
        """
        A convenience method as it allows to call the command as if it was 
        an object.
        Returns
            Callable object that will execute call _call_process with your arguments.
        """
        if name[:1] == '_':
            raise AttributeError(name)
        return lambda *args, **kwargs: self._call_process(name, *args, **kwargs)

    @property
    def get_dir(self):
        """
        Returns
            Git directory we are working on
        """
        return self.git_dir

    def execute(self, command,
                istream=None,
                with_keep_cwd=False,
                with_extended_output=False,
                with_exceptions=True,
                with_raw_output=False,
                ):
        """
        Handles executing the command on the shell and consumes and returns
        the returned information (stdout)

        ``command``
            The command argument list to execute.
            It should be a string, or a sequence of program arguments. The
            program to execute is the first item in the args sequence or string.

        ``istream``
            Standard input filehandle passed to subprocess.Popen.

        ``with_keep_cwd``
            Whether to use the current working directory from os.getcwd().
            GitPython uses get_work_tree() as its working directory by
            default and get_git_dir() for bare repositories.

        ``with_extended_output``
            Whether to return a (status, stdout, stderr) tuple.

        ``with_exceptions``
            Whether to raise an exception when git returns a non-zero status.

        ``with_raw_output``
            Whether to avoid stripping off trailing whitespace.

        Returns::
        
         str(output)                                  # extended_output = False (Default)
         tuple(int(status), str(stdout), str(stderr)) # extended_output = True
        
        Raise
        	GitCommandError
        
        NOTE
           If you add additional keyword arguments to the signature of this method, 
           you must update the execute_kwargs tuple housed in this module.
        """
        if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full':
            print ' '.join(command)

        # Allow the user to have the command executed in their working dir.
        if with_keep_cwd or self.git_dir is None:
          cwd = os.getcwd()
        else:
          cwd=self.git_dir

        # Start the process
        proc = subprocess.Popen(command,
                                cwd=cwd,
                                stdin=istream,
                                stderr=subprocess.PIPE,
                                stdout=subprocess.PIPE,
                                **extra
                                )

        # Wait for the process to return
        try:
            stdout_value = proc.stdout.read()
            stderr_value = proc.stderr.read()
            status = proc.wait()
        finally:
            proc.stdout.close()
            proc.stderr.close()

        # Strip off trailing whitespace by default
        if not with_raw_output:
            stdout_value = stdout_value.rstrip()
            stderr_value = stderr_value.rstrip()

        if with_exceptions and status != 0:
            raise GitCommandError(command, status, stderr_value)

        if GIT_PYTHON_TRACE == 'full':
            if stderr_value:
              print "%s -> %d: '%s' !! '%s'" % (command, status, stdout_value, stderr_value)
            elif stdout_value:
              print "%s -> %d: '%s'" % (command, status, stdout_value)
            else:
              print "%s -> %d" % (command, status)

        # Allow access to the command's status code
        if with_extended_output:
            return (status, stdout_value, stderr_value)
        else:
            return stdout_value

    def transform_kwargs(self, **kwargs):
        """
        Transforms Python style kwargs into git command line options.
        """
        args = []
        for k, v in kwargs.items():
            if len(k) == 1:
                if v is True:
                    args.append("-%s" % k)
                elif type(v) is not bool:
                    args.append("-%s%s" % (k, v))
            else:
                if v is True:
                    args.append("--%s" % dashify(k))
                elif type(v) is not bool:
                    args.append("--%s=%s" % (dashify(k), v))
        return args

    def _call_process(self, method, *args, **kwargs):
        """
        Run the given git command with the specified arguments and return
        the result as a String

        ``method``
            is the command. Contained "_" characters will be converted to dashes,
            such as in 'ls_files' to call 'ls-files'.

        ``args``
            is the list of arguments

        ``kwargs``
            is a dict of keyword arguments.
            This function accepts the same optional keyword arguments
            as execute().

        Examples::
            git.rev_list('master', max_count=10, header=True)

        Returns
            Same as execute()
        """

        # Handle optional arguments prior to calling transform_kwargs
        # otherwise these'll end up in args, which is bad.
        _kwargs = {}
        for kwarg in execute_kwargs:
            try:
                _kwargs[kwarg] = kwargs.pop(kwarg)
            except KeyError:
                pass

        # Prepare the argument list
        opt_args = self.transform_kwargs(**kwargs)
        ext_args = map(str, args)
        args = opt_args + ext_args

        call = ["git", dashify(method)]
        call.extend(args)

        return self.execute(call, **_kwargs)

@@ -437,16 +437,25 @@
 args = (
+'input', 
 'istream
@@ -498,16 +498,16 @@
 utput',%0A
-
         
@@ -2056,20 +2056,31 @@
 istream=
-None
+subprocess.PIPE
 ,%0A      
@@ -2223,32 +2223,60 @@
 w_output=False,%0A
+                input=None,%0A
                 
@@ -2723,16 +2723,77 @@
 Popen.%0A%0A
+        %60%60input%60%60%0A            String to be passed to stdin.%0A%0A
         
@@ -4448,70 +4448,22 @@
     
-try:%0A            stdout_value = proc.stdout.read()%0A           
+(stdout_value,
  std
@@ -4471,24 +4471,25 @@
 rr_value
+)
  = proc.
 stderr.r
@@ -4484,26 +4484,27 @@
 roc.
-stderr.read()%0A    
+communicate(input)%0A
     
@@ -4525,95 +4525,18 @@
 roc.
-wait()%0A        finally:%0A            proc.stdout.close()%0A            proc.stderr.close()
+returncode
 %0A%0A  

add 'input' argument to git commands.