diff --git a/rbtools/clients/__init__.py b/rbtools/clients/__init__.py
index 75db303767d1216c2e1aae2e7f2bfb7146a69902..a0ca4ac082cf3069625ad928ac3dc5d65209d0c1 100644
--- a/rbtools/clients/__init__.py
+++ b/rbtools/clients/__init__.py
@@ -12,6 +12,22 @@ from rbtools.utils.process import die, execute
 SCMCLIENTS = None
 
 
+class PatchResult(object):
+    """The result of a patch operation.
+
+    This stores state on whether the patch could be applied (fully or
+    partially), whether there are conflicts that can be resolved (as in
+    conflict markers, not reject files), which files conflicted, and the
+    patch output.
+    """
+    def __init__(self, applied, has_conflicts=False,
+                 conflicting_files=[], patch_output=None):
+        self.applied = applied
+        self.has_conflicts = has_conflicts
+        self.conflicting_files = conflicting_files
+        self.patch_output = patch_output
+
+
 class SCMClient(object):
     """
     A base representation of an SCM tool for fetching repository information
@@ -153,12 +169,12 @@ class SCMClient(object):
         else:
             return files
 
-    def _execute(self, cmd):
+    def _execute(self, cmd, *args, **kwargs):
         """
         Prints the results of the executed command and returns
         the data result from execute.
         """
-        return execute(cmd, ignore_errors=True)
+        return execute(cmd, ignore_errors=True, *args, **kwargs)
 
     def has_pending_changes(self):
         """Checks if there are changes waiting to be committed.
diff --git a/rbtools/clients/git.py b/rbtools/clients/git.py
index 36d6296b6cbbc99c592bea3dfac5fbb8c5c6c50d..cc41d879e026b8ee8ab174f63fc8cecb87e7fb3d 100644
--- a/rbtools/clients/git.py
+++ b/rbtools/clients/git.py
@@ -3,7 +3,7 @@ import os
 import re
 import sys
 
-from rbtools.clients import SCMClient, RepositoryInfo
+from rbtools.clients import PatchResult, SCMClient, RepositoryInfo
 from rbtools.clients.errors import (InvalidRevisionSpecError,
                                     TooManyRevisionsError)
 from rbtools.clients.perforce import PerforceClient
@@ -615,12 +615,29 @@ class GitClient(SCMClient):
         This will take the given patch file and apply it to the index,
         scheduling all changes for commit.
         """
+        cmd = ['git', 'apply', '-3']
+
         if p:
-            cmd = ['git', 'apply', '--index', '-p', p, patch_file]
+            cmd += ['-p', p]
+
+        cmd.append(patch_file)
+
+        rc, data = self._execute(cmd, with_errors=True, return_error_code=True)
+
+        if rc == 0:
+            return PatchResult(applied=True, patch_output=data)
+        elif 'with conflicts' in data:
+            return PatchResult(
+                applied=True,
+                has_conflicts=True,
+                conflicting_files=[
+                    line.split(' ', 1)[1]
+                    for line in data.splitlines()
+                    if line.startswith('U')
+                ],
+                patch_output=data)
         else:
-            cmd = ['git', 'apply', '--index', patch_file]
-
-        self._execute(cmd)
+            return PatchResult(applied=False, patch_output=data)
 
     def create_commit(self, message, author, files=[], all_files=False):
         modified_message = edit_text(message)
diff --git a/rbtools/clients/mercurial.py b/rbtools/clients/mercurial.py
index 46868c46d71ea6752d74d60bcef8c4f10462b0a1..947dc7007f78b8fb67984f4a124969b50ba494ee 100644
--- a/rbtools/clients/mercurial.py
+++ b/rbtools/clients/mercurial.py
@@ -5,7 +5,7 @@ import uuid
 
 from urlparse import urlsplit, urlunparse
 
-from rbtools.clients import SCMClient, RepositoryInfo
+from rbtools.clients import PatchResult, SCMClient, RepositoryInfo
 from rbtools.clients.errors import (InvalidRevisionSpecError,
                                     TooManyRevisionsError)
 from rbtools.clients.svn import SVNClient
@@ -614,12 +614,16 @@ class MercurialClient(SCMClient):
         This will take the given patch file and apply it to the working
         directory.
         """
+        cmd = ['hg', 'patch', '--no-commit']
+
         if p:
-            cmd = ['hg', 'patch', '--no-commit', '-p', p, patch_file]
-        else:
-            cmd = ['hg', 'patch', '--no-commit', patch_file]
+            cmd += ['-p', p]
+
+        cmd.append(patch_file)
+
+        rc, data = self._execute(cmd, with_errors=True, return_error_code=True)
 
-        self._execute(cmd)
+        return PatchResult(applied=(rc == 0), patch_output=data)
 
     def _apply_patch_for_empty_files(self, patch, p_num):
         """Returns True if any empty files in the patch are applied.
diff --git a/rbtools/commands/patch.py b/rbtools/commands/patch.py
index 31f42c098883365a3dd2766dc5c62af143615072..f7d5c02e0f9a566cfef03a4f7921ccf20a267713 100644
--- a/rbtools/commands/patch.py
+++ b/rbtools/commands/patch.py
@@ -77,9 +77,40 @@ class Patch(Command):
                     diff_file_path, base_dir):
         """Apply patch patch_file and display results to user."""
         print ("Patch is being applied from request %s with diff revision "
-               " %s." % (request_id, diff_revision))
-        tool.apply_patch(diff_file_path, repository_info.base_path,
-                         base_dir, self.options.px)
+               "%s." % (request_id, diff_revision))
+
+        result = tool.apply_patch(diff_file_path, repository_info.base_path,
+                                  base_dir, self.options.px)
+
+        if result.patch_output:
+            print
+            print result.patch_output.strip()
+            print
+
+        if not result.applied:
+            raise CommandError(
+                'Unable to apply the patch. The patch may be invalid, or '
+                'there may be conflicts that could not be resolvd.')
+
+        if result.has_conflicts:
+            if result.conflicting_files:
+                print ('The patch was partially applied, but there were '
+                       'conflicts in:')
+                print
+
+                for filename in result.conflicting_files:
+                    print '    %s' % filename
+
+                print
+            else:
+                print ('The patch was partially applied, but there were '
+                       'conflicts.')
+
+            return False
+        else:
+            print 'Successfully applied patch.'
+
+            return True
 
     def _extract_commit_message(self, review_request):
         """Returns a commit message based on the review request.
@@ -138,10 +169,10 @@ class Patch(Command):
                 pass
 
             tmp_patch_file = make_tempfile(diff_body)
-            self.apply_patch(repository_info, tool, request_id, diff_revision,
-                             tmp_patch_file, base_dir)
+            success = self.apply_patch(repository_info, tool, request_id,
+                                       diff_revision, tmp_patch_file, base_dir)
 
-            if self.options.commit:
+            if success and self.options.commit:
                 try:
                     review_request = api_root.get_review_request(
                         review_request_id=request_id,
diff --git a/rbtools/utils/process.py b/rbtools/utils/process.py
index c19f02d30cdc20617b75f97e6fb125b378412eed..8c735a753a9eb925e9bf22088996d8a51883c120 100644
--- a/rbtools/utils/process.py
+++ b/rbtools/utils/process.py
@@ -26,7 +26,8 @@ def execute(command,
             extra_ignore_errors=(),
             translate_newlines=True,
             with_errors=True,
-            none_on_ignored_error=False):
+            none_on_ignored_error=False,
+            return_error_code=False):
     """
     Utility function to execute a command and return the output.
     """
@@ -82,6 +83,9 @@ def execute(command,
                       % (rc, command, data))
 
     if rc and none_on_ignored_error:
-        return None
+        data = None
 
-    return data
+    if return_error_code:
+        return rc, data
+    else:
+        return data
