diff --git a/docs/rbtools/rbt/commands/post.txt b/docs/rbtools/rbt/commands/post.txt
index 1837635c79fbf0a64192a7e4a9e9f76933ba6ac0..134f2720c2b2fc9aac72d89337e91c84a11908a9 100644
--- a/docs/rbtools/rbt/commands/post.txt
+++ b/docs/rbtools/rbt/commands/post.txt
@@ -140,6 +140,11 @@ type::
 
    $ rbt post -r 42
 
+You can also use the :option:`-u` parameter to automatically determine the
+existing review request based on the patch summary and description::
+
+    $ rbt post -u
+
 Additionally, :command:`rbt post` can generate a summary and description for
 your review request based on the commit messages from the involved commits.
 This is accomplished using the :option:`-g` parameter. To post a new review
@@ -345,6 +350,10 @@ Options
 
    Existing review request ID to update.
 
+.. cmdoption:: -u, --update
+
+   Determine the existing review request to update.
+
 .. cmdoption:: --server
 
    Specify a different Review Board server to use.
diff --git a/rbtools/clients/__init__.py b/rbtools/clients/__init__.py
index 130da3d0be4d3b799d4c33a4b84487542ba93afd..8cdfff09d35e867a2607eb8c2a1e7306d759ebf0 100644
--- a/rbtools/clients/__init__.py
+++ b/rbtools/clients/__init__.py
@@ -179,6 +179,28 @@ class SCMClient(object):
         """
         raise NotImplementedError
 
+    def extract_summary(self, revision_range=None):
+        """Returns the summary from the commits on the current branch.
+
+        Derived classes should override this method if they wish to support
+        summary guessing.
+
+        If a derived class is unable to guess the summary, ``None`` should be
+        returned.
+        """
+        raise NotImplementedError
+
+    def extract_description(self, revision_range=None):
+        """Returns the description based on commits on the current branch.
+
+        Derived classes should override this method if they wish to support
+        description guessing.
+
+        If a derived class is unable to guess the description, ``None`` should
+        be returned.
+        """
+        raise NotImplementedError
+
 
 class RepositoryInfo(object):
     """
diff --git a/rbtools/clients/bazaar.py b/rbtools/clients/bazaar.py
index 8551fd40782740702f93fd7e2480ce854cc46e8c..bb6ba0409f4acaf78ba53624e169959a92f8a5d1 100644
--- a/rbtools/clients/bazaar.py
+++ b/rbtools/clients/bazaar.py
@@ -54,21 +54,24 @@ class BazaarClient(SCMClient):
 
         return repository_info
 
+    def get_default_revision_range(self):
+        """Returns the revision range based on the parent branch."""
+        if self.options.parent_branch:
+            return "ancestor:%s.." % self.options.parent_branch
+        else:
+            return "submit:.."
+
     def diff(self, files):
-        """
-        Return the diff of this branch with respect to its parent and set
-        the summary and description is required.
+        """Returns the diff of this branch with respect to its parent.
+
+        Additionally sets the summary and description as required.
         """
         files = files or []
 
-        if self.options.parent_branch:
-            revision_range = "ancestor:%s.." % self.options.parent_branch
-        else:
-            revision_range = "submit:.."
-
+        revision_range = self.get_default_revision_range()
         # Getting the diff for the changes in the current branch:
         diff = self._get_range_diff(revision_range, files)
-        self._set_summary("-1")
+        self._set_summary()
         self._set_description(revision_range)
 
         return {
@@ -76,15 +79,15 @@ class BazaarClient(SCMClient):
         }
 
     def diff_between_revisions(self, revision_range, files, repository_info):
-        """
-        Return the diff for the two revisions in ``revision_range`` and set
-        the summary and description is required.
+        """Returns the diff for the provided revision range.
+
+        The diff is generated for the two revisions in the provided revision
+        range. The summary and description are set as required.
         """
         diff = self._get_range_diff(revision_range, files)
 
         # Revision ranges in Bazaar and separated with dots, not colons:
-        last_revision = revision_range.split("..")[1]
-        self._set_summary(last_revision)
+        self._set_summary(revision_range)
         self._set_description(revision_range)
 
         return {
@@ -101,24 +104,35 @@ class BazaarClient(SCMClient):
 
         return diff
 
-    def _set_summary(self, revision):
-        """
-        Set the summary to the message of ``revision`` if asked to guess it.
+    def _set_summary(self, revision_range=None):
+        """Set the summary based on the ``revision_range``.
+
+        Extracts and sets the summary if guessing is enabled and summary is not
+        yet set.
         """
         if self.options.guess_summary and not self.options.summary:
-            self.options.summary = self._extract_summary(revision)
+            self.options.summary = self.extract_summary(revision_range)
 
     def _set_description(self, revision_range=None):
-        """
-        Set the description to the changelog of ``revision_range`` if asked to
-        guess it.
+        """Set the description based on the ``revision_range``.
+
+        Extracts and sets the description if guessing is enabled and
+        description is not yet set.
         """
         if self.options.guess_description and not self.options.description:
-            self.options.description = self._extract_description(
-                revision_range)
+            self.options.description = self.extract_description(revision_range)
+
+    def extract_summary(self, revision_range=None):
+        """Return the last commit message in ``revision_range``.
+
+        If revision_range is ``None``, the commit message of the last revision
+        in the repository is returned.
+        """
+        if revision_range:
+            revision = revision_range.split("..")[1]
+        else:
+            revision = '-1'
 
-    def _extract_summary(self, revision):
-        """Return the commit message for ``revision``."""
         # `bzr log --line' returns the log in the format:
         #   {revision-number}: {committer-name} {commit-date} {commit-message}
         # So we should ignore everything after the date (YYYY-MM-DD).
@@ -131,15 +145,11 @@ class BazaarClient(SCMClient):
 
         return summary
 
-    def _extract_description(self, revision_range=None):
+    def extract_description(self, revision_range=None):
         command = ["bzr"]
 
-        # If there is no revision range specified, that means we need the logs
-        # of all the outgoing changes:
-        if revision_range:
-            command.extend(["log", "-r", revision_range])
-        else:
-            command.extend(["missing", "-q", "--mine-only"])
+        revision_range = revision_range or self.get_default_revision_range()
+        command.extend(["log", "-r", revision_range])
 
         # We want to use the "short" output format, where all the logs are
         # separated with hyphens:
diff --git a/rbtools/clients/git.py b/rbtools/clients/git.py
index cbd28299f35cfe4ed45457491cf7430eb41c45d2..b71a3560e79e3c16c44c7f134a1fb9761f1dd30e 100644
--- a/rbtools/clients/git.py
+++ b/rbtools/clients/git.py
@@ -244,23 +244,86 @@ class GitClient(SCMClient):
 
         return None
 
-    def diff(self, args):
+    def extract_summary(self, revision_range=None):
+        """Extracts the summary based on the provided revision range."""
+        if not revision_range or ":" not in revision_range:
+            command = [self.git, "log", "--pretty=format:%s", "HEAD^!"]
+        else:
+            r1, r2 = revision_range.split(":")
+            command = [self.git, "log", "--pretty=format:%s", "%s^!" % r2]
+
+        return execute(command, ignore_errors=True).strip()
+
+    def extract_description(self, revision_range=None):
+        """Extracts the description based on the provided revision range."""
+        if revision_range and ":" not in revision_range:
+            command = [self.git, "log", "--pretty=format:%s%n%n%b",
+                       revision_range + ".."]
+        elif revision_range:
+            r1, r2 = revision_range.split(":")
+            command = [self.git, "log", "--pretty=format:%s%n%n%b",
+                       "%s..%s" % (r1, r2)]
+        else:
+            parent_branch = self.get_parent_branch()
+            head_ref = self.get_head_ref()
+            merge_base = self.get_merge_base(head_ref)
+            command = [self.git, "log", "--pretty=format:%s%n%n%b",
+                       (parent_branch or merge_base) + ".."]
+
+        return execute(command, ignore_errors=True).strip()
+
+    def _set_summary(self, revision_range=None):
+        """Sets the summary based on the provided revision range.
+
+        Extracts and sets the summary if guessing is enabled and summary is not
+        yet set.
         """
-        Performs a diff across all modified files in the branch, taking into
-        account a parent branch.
+        if (getattr(self.options, 'guess_summary', None) and
+                not getattr(self.options, 'summary', None)):
+            self.options.summary = self.extract_summary(revision_range)
+
+    def _set_description(self, revision_range=None):
+        """Sets the description based on the provided revision range.
+
+        Extracts and sets the description if guessing is enabled and
+        description is not yet set.
         """
+        if (getattr(self.options, 'guess_description', None) and
+                not getattr(self.options, 'description', None)):
+            self.options.description = self.extract_description(revision_range)
+
+    def get_parent_branch(self):
+        """Returns the parent branch."""
         if self.type == 'perforce':
             parent_branch = self.options.parent_branch or 'p4'
         else:
             parent_branch = self.options.parent_branch
 
+        return parent_branch
+
+    def get_head_ref(self):
+        """Returns the HEAD reference."""
         head_ref = "HEAD"
+
         if self.head_ref:
             head_ref = self.head_ref
 
-        self.merge_base = execute([self.git, "merge-base",
-                                   self.upstream_branch,
-                                   head_ref]).strip()
+        return head_ref
+
+    def get_merge_base(self, head_ref):
+        """Returns the merge base."""
+        return execute([self.git, "merge-base",
+                        self.upstream_branch,
+                        head_ref]).strip()
+
+    def diff(self, args):
+        """Performs a diff across all modified files in the branch.
+
+        The diff takes into account of the parent branch.
+        """
+        parent_branch = self.get_parent_branch()
+        head_ref = self.get_head_ref()
+        self.merge_base = self.get_merge_base(head_ref)
 
         if parent_branch:
             diff_lines = self.make_diff(parent_branch)
@@ -269,18 +332,8 @@ class GitClient(SCMClient):
             diff_lines = self.make_diff(self.merge_base, head_ref)
             parent_diff_lines = None
 
-        if (getattr(self.options, 'guess_summary', None) and
-            not getattr(self.options, 'summary', None)):
-            self.options.summary = execute(
-                [self.git, "log", "--pretty=format:%s", "HEAD^!"],
-                ignore_errors=True).strip()
-
-        if (getattr(self.options, 'guess_description', None) and
-            not getattr(self.options, 'description', None)):
-            self.options.description = execute(
-                [self.git, "log", "--pretty=format:%s%n%n%b",
-                 (parent_branch or self.merge_base) + ".."],
-                ignore_errors=True).strip()
+        self._set_summary()
+        self._set_description()
 
         return {
             'diff': diff_lines,
@@ -289,9 +342,7 @@ class GitClient(SCMClient):
         }
 
     def make_diff(self, ancestor, commit=""):
-        """
-        Performs a diff on a particular branch range.
-        """
+        """Performs a diff on a particular branch range."""
         if commit:
             rev_range = "%s..%s" % (ancestor, commit)
         else:
@@ -310,15 +361,12 @@ class GitClient(SCMClient):
             return self.make_perforce_diff(ancestor, diff_lines)
         elif self.type == "git":
             cmdline = [self.git, "diff", "--no-color", "--full-index",
-                       "--no-ext-diff", "--ignore-submodules"]
+                       "--no-ext-diff", "--ignore-submodules", "--no-renames",
+                       rev_range]
 
             if (self.capabilities is not None and
                 self.capabilities.has_capability('diffs', 'moved_files')):
-                cmdline.append('--find-renames')
-            else:
-                cmdline.append('--no-renames')
-
-            cmdline.append(rev_range)
+                cmdline.append('-M')
 
             return execute(cmdline)
 
@@ -436,16 +484,11 @@ class GitClient(SCMClient):
 
     def diff_between_revisions(self, revision_range, args, repository_info):
         """Perform a diff between two arbitrary revisions"""
-
-        head_ref = "HEAD"
-        if self.head_ref:
-            head_ref = self.head_ref
+        head_ref = self.get_head_ref()
 
         # Make a parent diff to the first of the revisions so that we
         # never end up with broken patches:
-        self.merge_base = execute([self.git, "merge-base",
-                                   self.upstream_branch,
-                                   head_ref]).strip()
+        self.merge_base = self.get_merge_base(head_ref)
 
         if ":" not in revision_range:
             # only one revision is specified
@@ -460,18 +503,6 @@ class GitClient(SCMClient):
                 parent_diff_lines = self.make_diff(self.merge_base,
                                                    revision_range)
 
-            if self.options.guess_summary and not self.options.summary:
-                self.options.summary = execute(
-                    [self.git, "log", "--pretty=format:%s", "HEAD^!"],
-                    ignore_errors=True).strip()
-
-            if (self.options.guess_description and
-                not self.options.description):
-                self.options.description = execute(
-                    [self.git, "log", "--pretty=format:%s%n%n%b",
-                     revision_range + ".."],
-                    ignore_errors=True).strip()
-
             diff_lines = self.make_diff(revision_range)
         else:
             r1, r2 = revision_range.split(":")
@@ -484,20 +515,11 @@ class GitClient(SCMClient):
             if not pdiff_required:
                 parent_diff_lines = self.make_diff(self.merge_base, r1)
 
-            if self.options.guess_summary and not self.options.summary:
-                self.options.summary = execute(
-                    [self.git, "log", "--pretty=format:%s", "%s^!" % r2],
-                    ignore_errors=True).strip()
-
-            if (self.options.guess_description and
-                not self.options.description):
-                self.options.description = execute(
-                    [self.git, "log", "--pretty=format:%s%n%n%b",
-                     "%s..%s" % (r1, r2)],
-                    ignore_errors=True).strip()
-
             diff_lines = self.make_diff(r1, r2)
 
+        self._set_summary(revision_range)
+        self._set_description(revision_range)
+
         return {
             'diff': diff_lines,
             'parent_diff': parent_diff_lines,
diff --git a/rbtools/clients/mercurial.py b/rbtools/clients/mercurial.py
index 2de3a3374fabdfadfc32e70a4e454fdc67431e14..df8b4ce2f274174e1887afdbab5bb1e33dd7e0e2 100644
--- a/rbtools/clients/mercurial.py
+++ b/rbtools/clients/mercurial.py
@@ -123,19 +123,34 @@ class MercurialClient(SCMClient):
             key, value = line.split('=', 1)
             self.hgrc[key] = value.strip()
 
-    def extract_summary(self, revision):
+    def extract_summary(self, revision_range=None):
         """
         Extracts the first line from the description of the given changeset.
         """
+        if revision_range:
+            revision = self._extract_revisions(revision_range)[1]
+        elif self._type == 'svn':
+            revision = "."
+        else:
+            revision = self._get_bottom_and_top_outgoing_revs_for_remote()[1]
+
         return execute(
             ['hg', 'log', '-r%s' % revision, '--template', '{desc|firstline}'],
             env=self._hg_env).replace('\n', ' ')
 
-    def extract_description(self, rev1, rev2):
+    def extract_description(self, revision_range=None):
         """
         Extracts all descriptions in the given revision range and concatenates
         them, most recent ones going first.
         """
+        if revision_range:
+            rev1, rev2 = self._extract_revisions(revision_range)
+        elif self._type == 'svn':
+            rev1 = self._get_parent_for_hgsubversion()
+            rev2 = "."
+        else:
+            rev1, rev2 = self._get_bottom_and_top_outgoing_revs_for_remote()
+
         numrevs = len(execute([
             'hg', 'log', '-r%s:%s' % (rev2, rev1),
             '--follow', '--template', r'{rev}\n'], env=self._hg_env
@@ -157,18 +172,22 @@ class MercurialClient(SCMClient):
         else:
             return self._get_outgoing_diff(files)
 
-    def _get_hgsubversion_diff(self, files):
-        parent = execute(['hg', 'parent', '--svn', '--template',
-                          '{node}\n']).strip()
+    def _get_parent_for_hgsubversion(self):
+        """Returns the parent Subversion branch.
 
-        if self.options.parent_branch:
-            parent = self.options.parent_branch
+        Returns the parent branch defined in the command options if it exists,
+        otherwise returns the parent Subversion branch of the current
+        repository.
+        """
+        return (getattr(self.options, 'parent_branch', None) or
+                execute(['hg', 'parent', '--svn', '--template',
+                        '{node}\n']).strip())
 
-        if self.options.guess_summary and not self.options.summary:
-            self.options.summary = self.extract_summary(".")
+    def _get_hgsubversion_diff(self, files):
+        self._set_summary()
+        self._set_description()
 
-        if self.options.guess_description and not self.options.description:
-            self.options.description = self.extract_description(parent, ".")
+        parent = self._get_parent_for_hgsubversion()
 
         if len(files) == 1:
             rs = "-r%s:%s" % (parent, files[0])
@@ -179,6 +198,43 @@ class MercurialClient(SCMClient):
             'diff': execute(["hg", "diff", "--svn", rs]),
         }
 
+    def _get_remote_branch(self):
+        """Returns the remote branch assoicated with this repository.
+
+        If the remote branch is not defined, the parent branch of the
+        repository is returned.
+        """
+        remote = self._remote_path[0]
+
+        if not remote and self.options.parent_branch:
+            remote = self.options.parent_branch
+
+        return remote
+
+    def _get_current_branch(self):
+        """Returns the current branch of this repository."""
+        return execute(['hg', 'branch'], env=self._hg_env).strip()
+
+    def _get_bottom_and_top_outgoing_revs_for_remote(self):
+        """Returns the bottom and top outgoing revisions.
+
+        Returns the bottom and top outgoing revisions for the changesets
+        between the current branch and the remote branch.
+        """
+        remote = self._get_remote_branch()
+        current_branch = self._get_current_branch()
+        outgoing_changesets = \
+            self._get_outgoing_changesets(current_branch, remote)
+
+        if outgoing_changesets:
+            top_rev, bottom_rev = \
+                self._get_top_and_bottom_outgoing_revs(outgoing_changesets)
+        else:
+            top_rev = None
+            bottom_rev = None
+
+        return bottom_rev, top_rev
+
     def _get_outgoing_diff(self, files):
         """
         When working with a clone of a Mercurial remote, we need to find
@@ -207,30 +263,10 @@ class MercurialClient(SCMClient):
         can just punish developers for doing such nonsense :)
         """
         files = files or []
-
-        remote = self._remote_path[0]
-
-        if not remote and self.options.parent_branch:
-            remote = self.options.parent_branch
-
-        current_branch = execute(['hg', 'branch'], env=self._hg_env).strip()
-
-        outgoing_changesets = \
-            self._get_outgoing_changesets(current_branch, remote)
-
-        if outgoing_changesets:
-            top_rev, bottom_rev = \
-                self._get_top_and_bottom_outgoing_revs(outgoing_changesets)
-        else:
-            top_rev = None
-            bottom_rev = None
-
-        if self.options.guess_summary and not self.options.summary:
-            self.options.summary = self.extract_summary(top_rev)
-
-        if self.options.guess_description and not self.options.description:
-            self.options.description = self.extract_description(bottom_rev,
-                                                                top_rev)
+        self._set_summary()
+        self._set_description()
+        bottom_rev, top_rev = \
+            self._get_bottom_and_top_outgoing_revs_for_remote()
 
         if bottom_rev is not None and top_rev is not None:
             full_command = ['hg', 'diff', '-r', str(bottom_rev), '-r',
@@ -308,13 +344,8 @@ class MercurialClient(SCMClient):
 
         return top_rev, bottom_rev
 
-    def diff_between_revisions(self, revision_range, args, repository_info):
-        """
-        Performs a diff between 2 revisions of a Mercurial repository.
-        """
-        if self._type != 'hg':
-            raise NotImplementedError
-
+    def _extract_revisions(self, revision_range):
+        """Returns the revisions from the provided revision range."""
         if ':' in revision_range:
             r1, r2 = revision_range.split(':')
         else:
@@ -327,11 +358,35 @@ class MercurialClient(SCMClient):
             r1 = execute(["hg", "parents", "-r", r2,
                           "--template", "{rev}\n"]).split()[0]
 
+        return r1, r2
+
+    def _set_summary(self, revision_range=None):
+        """Sets the summary based on the provided revision range.
+
+        Extracts and sets the summary if guessing is enabled and summary is not
+        yet set.
+        """
         if self.options.guess_summary and not self.options.summary:
-            self.options.summary = self.extract_summary(r2)
+            self.options.summary = self.extract_summary(revision_range)
 
+    def _set_description(self, revision_range=None):
+        """Sets the description based on the provided revision range.
+
+        Extracts and sets the description if guessing is enabled and
+        description is not yet set.
+        """
         if self.options.guess_description and not self.options.description:
-            self.options.description = self.extract_description(r1, r2)
+            self.options.description = self.extract_description(revision_range)
+
+    def diff_between_revisions(self, revision_range, args, repository_info):
+        """Performs a diff between 2 revisions of a Mercurial repository."""
+        if self._type != 'hg':
+            raise NotImplementedError
+
+        self._set_summary(revision_range)
+        self._set_description(revision_range)
+
+        r1, r2 = self._extract_revisions(revision_range)
 
         return {
             'diff': execute(["hg", "diff", "-r", r1, "-r", r2],
diff --git a/rbtools/commands/post.py b/rbtools/commands/post.py
index 6299a023facf47cca715702cd4f573dfee9af03b..18636d8185c375081ce485749e495069b6ee7e69 100644
--- a/rbtools/commands/post.py
+++ b/rbtools/commands/post.py
@@ -2,10 +2,27 @@ import logging
 import os
 import re
 import sys
+from difflib import SequenceMatcher
 
 from rbtools.api.errors import APIError
 from rbtools.commands import Command, CommandError, Option
+from rbtools.utils.console import confirm
 from rbtools.utils.diffs import get_diff
+from rbtools.utils.repository import get_repository_id
+from rbtools.utils.users import get_user
+
+
+class Score(object):
+    """Encapsulates ranking information for matching existing requests."""
+    EXACT_MATCH_SCORE = 1.0
+
+    def __init__(self, summary_score, description_score):
+        self.summary_score = summary_score
+        self.description_score = description_score
+
+    def is_exact_match(self):
+        return (self.summary_score == self.EXACT_MATCH_SCORE and
+                self.description_score == self.EXACT_MATCH_SCORE)
 
 
 class Post(Command):
@@ -20,6 +37,11 @@ class Post(Command):
                metavar="ID",
                default=None,
                help="existing review request ID to update"),
+        Option('-u', '--update',
+               dest="update",
+               action="store_true",
+               default=False,
+               help="determine existing review request to update"),
         Option("--server",
                dest="server",
                metavar="SERVER",
@@ -238,6 +260,9 @@ class Post(Command):
             self.options.guess_summary = True
             self.options.guess_description = True
 
+        if self.options.rid and self.options.update:
+            self.options.update = False
+
         if self.options.testing_done and self.options.testing_file:
             raise CommandError("The --testing-done and --testing-done-file "
                                "options are mutually exclusive.\n")
@@ -291,33 +316,150 @@ class Post(Command):
 
         return repository_info.path
 
+    def get_match_score(self, summary_pair, description_pair):
+        """Get a score based on a pair of summaries and a pair of descriptions.
+
+        The scores for summary and description pairs are calculated
+        independently using SequenceMatcher, and returned as part of a Score
+        object.
+        """
+        if not summary_pair or not description_pair:
+            return None
+
+        summary_score = SequenceMatcher(
+            None, summary_pair[0], summary_pair[1]).ratio()
+        description_score = SequenceMatcher(
+            None, description_pair[0], description_pair[1]).ratio()
+
+        return Score(summary_score, description_score)
+
+    def get_possible_matches(self, review_requests, summary, description,
+                             limit=5):
+        """Returns a sorted list of tuples of score and review request.
+
+        Each review request is given a score based on the summary and
+        description provided. The result is a sorted list of tuples containing
+        the score and the corresponding review request, sorted by the highest
+        scoring review request first.
+        """
+        candidates = []
+
+        # Get all potential matches.
+        try:
+            while True:
+                for review_request in review_requests:
+                    summary_pair = (review_request.summary, summary)
+                    description_pair = (review_request.description, description)
+                    score = self.get_match_score(
+                        summary_pair, description_pair)
+                    candidates.append((score, review_request))
+
+                review_requests = review_requests.get_next()
+        except StopIteration:
+            pass
+
+        # Sort by summary and description on descending rank.
+        sorted_candidates = sorted(
+            candidates,
+            key=lambda m: (m[0].summary_score, m[0].description_score),
+            reverse=True
+        )
+
+        return sorted_candidates[:limit]
+
+    def num_exact_matches(self, possible_matches):
+        """Returns the number of exact matches in the possible match list."""
+        count = 0
+
+        for score, request in possible_matches:
+            if score.is_exact_match():
+                count += 1
+
+        return count
+
+    def guess_existing_review_request_id(self, repository_info, api_root,
+                                         api_client, tool, revision_range):
+        """Try to guess the existing review request ID if it is available.
+
+        The existing review request is guessed by comparing the existing
+        summary and description to the current post's summary and description,
+        respectively. The current post's summary and description are guessed if
+        they are not provided.
+
+        If the summary and description exactly match those of an existing
+        review request, the ID for which is immediately returned. Otherwise,
+        the user is prompted to select from a list of potential matches,
+        sorted by the highest ranked match first.
+        """
+        user = get_user(api_client, api_root, auth_required=True)
+        repository_id = get_repository_id(
+            repository_info, api_root, self.options.repository_url)
+
+        try:
+            # Get only pending requests by the current user for this
+            # repository.
+            review_requests = api_root.get_review_requests(
+                repository=repository_id, from_user=user.username,
+                status='pending')
+
+            if not review_requests:
+                raise CommandError('No existing review requests to update for '
+                                   'user %s.'
+                                   % user.username)
+        except APIError, e:
+            raise CommandError('Error getting review requests for user '
+                               '%s: %s' % (user.name, e))
+
+        try:
+            summary = (getattr(self.options, 'summary', None) or
+                       tool.extract_summary(revision_range))
+            description = (getattr(self.options, 'description', None) or
+                           tool.extract_description(revision_range))
+        except NotImplementedError:
+            raise CommandError('--summary and --description are required.')
+
+        possible_matches = self.get_possible_matches(review_requests, summary,
+                                                     description)
+        exact_match_count = self.num_exact_matches(possible_matches)
+
+        for score, request in possible_matches:
+            # If the score is the only exact match, return the review request
+            # ID without confirmation, otherwise prompt.
+            if score.is_exact_match() and exact_match_count == 1:
+                return request.id
+            else:
+                question = ("Update Review Request #%s: '%s'? "
+                            % (request.id, request.summary))
+
+                if confirm(question):
+                    return request.id
+
+        return None
+
     def post_request(self, tool, repository_info, server_url, api_root,
-                     changenum=None, diff_content=None,
+                     review_request_id=None, changenum=None, diff_content=None,
                      parent_diff_content=None, base_commit_id=None,
                      submit_as=None, retries=3):
         """Creates or updates a review request, and uploads a diff.
 
         On success the review request id and url are returned.
         """
-        if self.options.rid:
-            # Retrieve the review request coresponding to the user
-            # provided id.
+        if review_request_id:
+            # Retrieve the review request corresponding to the provided id.
             try:
                 review_request = api_root.get_review_request(
-                    review_request_id=self.options.rid)
+                    review_request_id=review_request_id)
             except APIError, e:
-                raise CommandError("Error getting review request %s: %s" % (
-                    self.options.rid, e))
+                raise CommandError("Error getting review request %s: %s"
+                                   % (review_request_id, e))
 
             if review_request.status == 'submitted':
                 raise CommandError(
                     "Review request %s is marked as %s. In order to update "
-                    "it, please reopen the request and try again." % (
-                        self.options.rid,
-                        review_request.status))
+                    "it, please reopen the review request and try again."
+                    % (review_request_id, review_request.status))
         else:
-            # The user did not provide a request id, so we will create
-            # a new review request.
+            # No review_request_id, so we will create a new review request.
             try:
                 repository = (
                     self.options.repository_url or
@@ -492,11 +634,21 @@ class Post(Command):
         else:
             changenum = None
 
+        if self.options.update:
+            self.options.rid = self.guess_existing_review_request_id(
+                repository_info, api_root, api_client, tool,
+                self.options.revision_range)
+
+            if not self.options.rid:
+                raise CommandError('Could not determine the existing review '
+                                   'request to update.')
+
         request_id, review_url = self.post_request(
             tool,
             repository_info,
             server_url,
             api_root,
+            self.options.rid,
             changenum=changenum,
             diff_content=diff,
             parent_diff_content=parent_diff,
