diff --git a/docs/rbtools/rbt/commands/diff.rst b/docs/rbtools/rbt/commands/diff.rst
index 2b49cdc98f47d8a6d4342ef6e76a86fee223d882..083aadbdd655f52cb68e40529369cc02ef36355a 100644
--- a/docs/rbtools/rbt/commands/diff.rst
+++ b/docs/rbtools/rbt/commands/diff.rst
@@ -60,7 +60,8 @@ Options
 
    Exclude all files that match the given pattern from the diff. This can be
    used multiple times to specify multiple patterns. This is currently only
-   available with some SCM backends (Bazaar, CVS, Git, Mercurial, and SVN).
+   available with some SCM backends (Bazaar, CVS, Git, Mercurial, Perforce,
+   and SVN).
 
    The ``EXCLUDE_PATTERNS`` option can be set in :file:`.reviewboardrc` and
    will have the same effect.
diff --git a/docs/rbtools/rbt/commands/post.rst b/docs/rbtools/rbt/commands/post.rst
index 46526abd794ced4fb190fb22162458acb525f1d3..7aa131191ebe571cf06970bd9dd218782c2cb1eb 100644
--- a/docs/rbtools/rbt/commands/post.rst
+++ b/docs/rbtools/rbt/commands/post.rst
@@ -621,7 +621,8 @@ Options
 
    Exclude all files that match the given pattern from the diff. This can be
    used multiple times to specify multiple patterns. This is currently only
-   available with some SCM backends (Bazaar, CVS, Git, Mercurial, and SVN).
+   available with some SCM backends (Bazaar, CVS, Git, Mercurial, Perforce,
+   and SVN).
 
    The ``EXCLUDE_PATTERNS`` option can be set in :file:`.reviewboardrc` and
    will have the same effect.
diff --git a/rbtools/clients/perforce.py b/rbtools/clients/perforce.py
index c0c187d3061b759fe93989d8c2300a867dac4920..11ec4a5bfeea45ec04562fcc9400a1377e92fcff 100644
--- a/rbtools/clients/perforce.py
+++ b/rbtools/clients/perforce.py
@@ -1,3 +1,4 @@
+import fnmatch
 import logging
 import marshal
 import os
@@ -162,6 +163,8 @@ class PerforceClient(SCMClient):
     """
     name = 'Perforce'
 
+    supports_diff_exclude_patterns = True
+
     supports_diff_extra_args = True
 
     DATE_RE = re.compile(r'(\w+)\s+(\w+)\s+(\d+)\s+(\d\d:\d\d:\d\d)\s+'
@@ -427,11 +430,13 @@ class PerforceClient(SCMClient):
         to take into account adds/deletes and to provide the necessary
         revision information.
         """
+        exclude_patterns = self._normalize_patterns(exclude_patterns)
+
         if not revisions:
             # The "path posting" is still interesting enough to keep around. If
             # the given arguments don't parse as valid changelists, fall back
             # on that behavior.
-            return self._path_diff(extra_args)
+            return self._path_diff(extra_args, exclude_patterns)
 
         # Support both //depot/... paths and local filenames. For the moment,
         # this does *not* support any of perforce's traversal literals like ...
@@ -458,7 +463,8 @@ class PerforceClient(SCMClient):
                          'to %s',
                          base, tip)
             return self._compute_range_changes(
-                base, tip, depot_include_files, local_include_files)
+                base, tip, depot_include_files, local_include_files,
+                exclude_patterns)
 
         # Strip off the prefix
         tip = tip[len(self.REVISION_PENDING_CLN_PREFIX):]
@@ -519,7 +525,9 @@ class PerforceClient(SCMClient):
             if ((depot_include_files and
                  depot_file not in depot_include_files) or
                 (local_include_files and
-                 local_file not in local_include_files)):
+                 local_file not in local_include_files) or
+                self._should_exclude_file(local_file, depot_file,
+                                          exclude_patterns)):
                 continue
 
             old_file = ''
@@ -606,7 +614,7 @@ class PerforceClient(SCMClient):
         }
 
     def _compute_range_changes(self, base, tip, depot_include_files,
-                               local_include_files):
+                               local_include_files, exclude_patterns):
         """Compute the changes across files given a revision range.
 
         This will look at the history of all changes within the given range and
@@ -712,6 +720,7 @@ class PerforceClient(SCMClient):
         # Now generate the diff
         supports_moves = self._supports_moves()
         diff_lines = []
+
         for f in files:
             action = f['action']
             depot_file = f['depotFile']
@@ -723,7 +732,9 @@ class PerforceClient(SCMClient):
             if ((depot_include_files and
                  depot_file not in depot_include_files) or
                 (local_include_files and
-                 local_file not in local_include_files)):
+                 local_file not in local_include_files) or
+                self._should_exclude_file(local_file, depot_file,
+                                          exclude_patterns)):
                 continue
 
             if action == 'add':
@@ -943,7 +954,7 @@ class PerforceClient(SCMClient):
 
         return old_filename, new_filename, new_depot_file
 
-    def _path_diff(self, args):
+    def _path_diff(self, args, exclude_patterns):
         """
         Process a path-style diff. This allows people to post individual files
         in various ways.
@@ -1051,6 +1062,11 @@ class PerforceClient(SCMClient):
                     changetype_short = 'M'
                     base_revision = int(first_record['rev'])
 
+                local_path = self._depot_to_local(depot_path)
+                if self._should_exclude_file(local_path, depot_path,
+                                             exclude_patterns):
+                    continue
+
                 # TODO: We're passing new_depot_file='' here just to make
                 # things work like they did before the moved file change was
                 # added (58ccae27). This section of code needs to be updated
@@ -1277,3 +1293,38 @@ class PerforceClient(SCMClient):
         return (self.capabilities and
                 self.capabilities.has_capability('scmtools', 'perforce',
                                                  'empty_files'))
+
+    def _should_exclude_file(self, local_file, depot_file, exclude_patterns):
+        """Determine if a file should be excluded from a diff.
+
+        Check if the file identified by (local_file, depot_file) should be
+        excluded from the diff. If a pattern beings with '//', then it will be
+        matched against the depot_file. Otherwise, it will be matched against
+        the local file.
+
+        This function expects `exclude_patterns` to be normalized.
+        """
+        for pattern in exclude_patterns:
+            if pattern.startswith('//'):
+                if fnmatch.fnmatch(depot_file, pattern):
+                    return True
+            elif fnmatch.fnmatch(local_file, pattern):
+                    return True
+
+        return False
+
+    def _normalize_patterns(self, patterns):
+        """Normalize the set of patterns so all non-depot paths are absolute.
+
+        A path with a leading // is interpreted as a depot pattern and remains
+        unchanged. All other paths are normalized to be absolute paths.
+        """
+        normalized_patterns = []
+
+        for pattern in patterns:
+            if pattern.startswith('//'):
+                normalized_patterns.append(pattern)
+            else:
+                normalized_patterns.append(os.path.abspath(pattern))
+
+        return normalized_patterns
