diff options
| -rw-r--r-- | lib/git/cmd.py | 57 | ||||
| -rw-r--r-- | lib/git/errors.py | 10 | ||||
| -rw-r--r-- | test/git/test_commit.py | 100 | ||||
| -rw-r--r-- | test/git/test_git.py | 24 |
4 files changed, 88 insertions, 103 deletions
diff --git a/lib/git/cmd.py b/lib/git/cmd.py index 029fcbc9..9c8b4010 100644 --- a/lib/git/cmd.py +++ b/lib/git/cmd.py @@ -78,9 +78,9 @@ class Git(MethodMissingMixin): def execute(self, command, istream=None, keep_cwd=False, - with_status=False, + extended_output=False, with_stderr=False, - with_exceptions=False, + with_exceptions=True, with_raw_output=False, ): """ @@ -98,11 +98,8 @@ class Git(MethodMissingMixin): GitPython uses get_work_tree() as its working directory by default and get_git_dir() for bare repositories. - ``with_status`` - Whether to return a (status, str) tuple. - - ``with_stderr`` - Whether to combine stderr into the output. + ``extended_output`` + Whether to return a (status, stdout, stderr) tuple. ``with_exceptions`` Whether to raise an exception when git returns a non-zero status. @@ -111,20 +108,13 @@ class Git(MethodMissingMixin): Whether to avoid stripping off trailing whitespace. Returns - str(output) # with_status = False (Default) - tuple(int(status), str(output)) # with_status = True + str(output) # extended_output = False (Default) + tuple(int(status), str(output)) # extended_output = True """ if GIT_PYTHON_TRACE and not GIT_PYTHON_TRACE == 'full': print ' '.join(command) - # Allow stderr to be merged into stdout when with_stderr is True. - # Otherwise, throw stderr away. - if with_stderr: - stderr = subprocess.STDOUT - else: - stderr = subprocess.PIPE - # Allow the user to have the command executed in their working dir. if keep_cwd: cwd = os.getcwd() @@ -135,28 +125,29 @@ class Git(MethodMissingMixin): proc = subprocess.Popen(command, cwd=cwd, stdin=istream, - stderr=stderr, + stderr=subprocess.PIPE, stdout=subprocess.PIPE ) # Wait for the process to return - stdout_value = proc.stdout.read() - status = proc.wait() - proc.stdout.close() - - if proc.stderr: - stderr_value = proc.stderr.read() - proc.stderr.close() + 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() + + print command, status - # Grab the exit status - status = proc.poll() if with_exceptions and status != 0: - raise GitCommandError("%s returned exit status %d" - % (str(command), status)) + print 19 + raise GitCommandError(command, status, stderr_value) if GIT_PYTHON_TRACE == 'full': if stderr_value: @@ -167,8 +158,8 @@ class Git(MethodMissingMixin): print "%s -> %d" % (command, status) # Allow access to the command's status code - if with_status: - return (status, stdout_value) + if extended_output: + return (status, stdout_value, stderr_value) else: return stdout_value @@ -217,9 +208,9 @@ class Git(MethodMissingMixin): # otherwise these'll end up in args, which is bad. istream = kwargs.pop("istream", None) keep_cwd = kwargs.pop("keep_cwd", None) - with_status = kwargs.pop("with_status", None) + extended_output = kwargs.pop("extended_output", None) with_stderr = kwargs.pop("with_stderr", None) - with_exceptions = kwargs.pop("with_exceptions", None) + with_exceptions = kwargs.pop("with_exceptions", True) with_raw_output = kwargs.pop("with_raw_output", None) # Prepare the argument list @@ -233,7 +224,7 @@ class Git(MethodMissingMixin): return self.execute(call, istream = istream, keep_cwd = keep_cwd, - with_status = with_status, + extended_output = extended_output, with_stderr = with_stderr, with_exceptions = with_exceptions, with_raw_output = with_raw_output, diff --git a/lib/git/errors.py b/lib/git/errors.py index f3fae26b..f0a4be1a 100644 --- a/lib/git/errors.py +++ b/lib/git/errors.py @@ -5,4 +5,12 @@ class NoSuchPathError(Exception): pass class GitCommandError(Exception): - pass + def __init__(self, command, status, stderr=None): + self.stderr = stderr + self.status = status + self.command = command + + def __str__(self): + return repr("%s returned exit status %d" % + (str(self.command), self.status)) + diff --git a/test/git/test_commit.py b/test/git/test_commit.py index 44b24c82..4504a360 100644 --- a/test/git/test_commit.py +++ b/test/git/test_commit.py @@ -8,13 +8,13 @@ class TestCommit(object): @patch(Git, 'method_missing') def test_bake(self, git): git.return_value = fixture('rev_list_single') - + commit = Commit(self.repo, **{'id': '4c8124ffcf4039d292442eeccabdeca5af5c5017'}) commit.author # bake - + assert_equal("Tom Preston-Werner", commit.author.name) assert_equal("tom@mojombo.com", commit.author.email) - + assert_true(git.called) assert_equal(git.call_args, (('rev_list', '4c8124ffcf4039d292442eeccabdeca5af5c5017'), {'pretty': 'raw', 'max_count': 1})) @@ -26,11 +26,11 @@ class TestCommit(object): @patch(Git, 'method_missing') def test_diff(self, git): git.return_value = fixture('diff_p') - + diffs = Commit.diff(self.repo, 'master') - + assert_equal(15, len(diffs)) - + assert_equal('.gitignore', diffs[0].a_path) assert_equal('.gitignore', diffs[0].b_path) assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_commit.id) @@ -39,7 +39,7 @@ class TestCommit(object): assert_equal(False, diffs[0].new_file) assert_equal(False, diffs[0].deleted_file) assert_equal("--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs[0].diff) - + assert_equal('lib/grit/actor.rb', diffs[5].a_path) assert_equal(None, diffs[5].a_commit) assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_commit.id) @@ -51,47 +51,47 @@ class TestCommit(object): @patch(Git, 'method_missing') def test_diff_with_two_commits(self, git): git.return_value = fixture('diff_2') - + diffs = Commit.diff(self.repo, '59ddc32', '13d27d5') - + assert_equal(3, len(diffs)) - + assert_true(git.called) - assert_equal(git.call_args, (('diff', '59ddc32', '13d27d5', '--', 'master'), {'full_index': True})) + assert_equal(git.call_args, (('diff', '59ddc32', '13d27d5'), {'full_index': True})) - @patch(Git, 'method_missing') + @patch(Git, 'method_missing') def test_diff_with_files(self, git): git.return_value = fixture('diff_f') - + diffs = Commit.diff(self.repo, '59ddc32', ['lib']) - + assert_equal(1, len(diffs)) assert_equal('lib/grit/diff.rb', diffs[0].a_path) - + assert_true(git.called) assert_equal(git.call_args, (('diff', '59ddc32', '--', 'lib'), {'full_index': True})) - - @patch(Git, 'method_missing') + + @patch(Git, 'method_missing') def test_diff_with_two_commits_and_files(self, git): git.return_value = fixture('diff_2f') - + diffs = Commit.diff(self.repo, '59ddc32', '13d27d5', ['lib']) - + assert_equal(1, len(diffs)) assert_equal('lib/grit/commit.rb', diffs[0].a_path) - + assert_true(git.called) assert_equal(git.call_args, (('diff', '59ddc32', '13d27d5', '--', 'lib'), {'full_index': True})) @patch(Git, 'method_missing') def test_diffs(self, git): git.return_value = fixture('diff_p') - + commit = Commit(self.repo, id='91169e1f5fa4de2eaea3f176461f5dc784796769', parents=['038af8c329ef7c1bae4568b98bd5c58510465493']) diffs = commit.diffs - + assert_equal(15, len(diffs)) - + assert_equal('.gitignore', diffs[0].a_path) assert_equal('.gitignore', diffs[0].b_path) assert_equal('4ebc8aea50e0a67e000ba29a30809d0a7b9b2666', diffs[0].a_commit.id) @@ -100,27 +100,27 @@ class TestCommit(object): assert_equal(False, diffs[0].new_file) assert_equal(False, diffs[0].deleted_file) assert_equal("--- a/.gitignore\n+++ b/.gitignore\n@@ -1 +1,2 @@\n coverage\n+pkg", diffs[0].diff) - + assert_equal('lib/grit/actor.rb', diffs[5].a_path) assert_equal(None, diffs[5].a_commit) assert_equal('f733bce6b57c0e5e353206e692b0e3105c2527f4', diffs[5].b_commit.id) assert_equal(True, diffs[5].new_file) - + assert_true(git.called) - assert_equal(git.call_args, (('diff', '038af8c329ef7c1bae4568b98bd5c58510465493', - '91169e1f5fa4de2eaea3f176461f5dc784796769', - '--', '59ddc32', '13d27d5', '--', 'master'), {'full_index': True})) + assert_equal(git.call_args, (('diff', '038af8c329ef7c1bae4568b98bd5c58510465493', + '91169e1f5fa4de2eaea3f176461f5dc784796769', + ), {'full_index': True})) - @patch(Git, 'method_missing') + @patch(Git, 'method_missing') def test_diffs_on_initial_import(self, git): git.return_value = fixture('diff_i') - + commit = Commit(self.repo, id='634396b2f541a9f2d58b00be1a07f0c358b999b3') commit.__bake_it__() diffs = commit.diffs - + assert_equal(10, len(diffs)) - + assert_equal('History.txt', diffs[0].a_path) assert_equal('History.txt', diffs[0].b_path) assert_equal(None, diffs[0].a_commit) @@ -129,61 +129,61 @@ class TestCommit(object): assert_equal(True, diffs[0].new_file) assert_equal(False, diffs[0].deleted_file) assert_equal("--- /dev/null\n+++ b/History.txt\n@@ -0,0 +1,5 @@\n+== 1.0.0 / 2007-10-09\n+\n+* 1 major enhancement\n+ * Birthday!\n+", diffs[0].diff) - + assert_equal('lib/grit.rb', diffs[5].a_path) assert_equal(None, diffs[5].a_commit) assert_equal('32cec87d1e78946a827ddf6a8776be4d81dcf1d1', diffs[5].b_commit.id) assert_equal(True, diffs[5].new_file) - + assert_true(git.called) assert_equal(git.call_args, (('show', '634396b2f541a9f2d58b00be1a07f0c358b999b3'), {'full_index': True, 'pretty': 'raw'})) - - @patch(Git, 'method_missing') + + @patch(Git, 'method_missing') def test_diffs_on_initial_import_with_empty_commit(self, git): git.return_value = fixture('show_empty_commit') - + commit = Commit(self.repo, id='634396b2f541a9f2d58b00be1a07f0c358b999b3') diffs = commit.diffs - + assert_equal([], diffs) - + assert_true(git.called) assert_equal(git.call_args, (('show', '634396b2f541a9f2d58b00be1a07f0c358b999b3'), {'full_index': True, 'pretty': 'raw'})) - - @patch(Git, 'method_missing') + + @patch(Git, 'method_missing') def test_diffs_with_mode_only_change(self, git): git.return_value = fixture('diff_mode_only') - + commit = Commit(self.repo, id='91169e1f5fa4de2eaea3f176461f5dc784796769') commit.__bake_it__() diffs = commit.diffs - + assert_equal(23, len(diffs)) assert_equal('100644', diffs[0].a_mode) assert_equal('100755', diffs[0].b_mode) - + assert_true(git.called) assert_equal(git.call_args, (('show', '91169e1f5fa4de2eaea3f176461f5dc784796769'), {'full_index': True, 'pretty': 'raw'})) - @patch(Git, 'method_missing') + @patch(Git, 'method_missing') def test_stats(self, git): git.return_value = fixture('diff_numstat') - + commit = Commit(self.repo, id='634396b2f541a9f2d58b00be1a07f0c358b999b3') commit.__bake_it__() stats = commit.stats - + keys = stats.files.keys() keys.sort() assert_equal(["a.txt", "b.txt"], keys) - + assert_true(git.called) assert_equal(git.call_args, (('diff', '634396b2f541a9f2d58b00be1a07f0c358b999b3'), {'numstat': True})) - + def test_str(self): commit = Commit(self.repo, id='abc') assert_equal ("abc", str(commit)) - + def test_repr(self): commit = Commit(self.repo, id='abc') assert_equal('<GitPython.Commit "abc">', repr(commit)) diff --git a/test/git/test_git.py b/test/git/test_git.py index e452e68b..0c074ec3 100644 --- a/test/git/test_git.py +++ b/test/git/test_git.py @@ -14,6 +14,11 @@ class TestGit(object): assert_true(git.called) # assert_equal(git.call_args, ((("%s version " % self.git_bin_base),), {})) + @raises(GitCommandError) + def test_it_raises_errors(self): + self.git.this_does_not_exist() + + def test_it_transforms_kwargs_into_git_command_arguments(self): assert_equal(["-s"], self.git.transform_kwargs(**{'s': True})) assert_equal(["-s5"], self.git.transform_kwargs(**{'s': 5})) @@ -33,25 +38,6 @@ class TestGit(object): self.git.hash_object(istream=fh, stdin=True)) fh.close() - def test_it_returns_status_and_ignores_stderr(self): - assert_equal((1, ""), self.git.this_does_not_exist(with_status=True)) - - @raises(GitCommandError) - def test_it_raises_errors(self): - self.git.this_does_not_exist(with_exceptions=True) - - def test_it_returns_stderr_in_output(self): - # Note: no trailiing newline - assert_match(r"^git: 'this-does-not-exist' is not a git-command", - self.git.this_does_not_exist(with_stderr=True)) - - def test_it_does_not_strip_output_when_using_with_raw_output(self): - # Note: trailing newline - assert_match(r"^git: 'this-does-not-exist' is not a git-command" \ - r"(\. See 'git --help'\.)?" + os.linesep, - self.git.this_does_not_exist(with_stderr=True, - with_raw_output=True)) - def test_it_handles_large_input(self): output = self.git.execute(["cat", "/bin/bash"]) assert_true(len(output) > 4096) # at least 4k |
