diff --git a/rbtools/clients/git.py b/rbtools/clients/git.py
index be4cbf18d8d33a291888e4547086ec823b16c1ae..df075af281040034516a700e7c3f2aa8b6727089 100644
--- a/rbtools/clients/git.py
+++ b/rbtools/clients/git.py
@@ -18,6 +18,7 @@ from rbtools.clients.errors import (AmendError,
                                     InvalidRevisionSpecError,
                                     TooManyRevisionsError,
                                     SCMError)
+from rbtools.clients.gitrepositoryhooks import GitRepositoryHooks
 from rbtools.clients.perforce import PerforceClient
 from rbtools.clients.svn import SVNClient, SVNRepositoryInfo
 from rbtools.utils.checks import check_install, is_valid_version
@@ -50,6 +51,8 @@ class GitClient(SCMClient):
     can_branch = True
     can_squash_merges = True
 
+    repository_hooks_class = GitRepositoryHooks
+
     TYPE_GIT = 0
     TYPE_GIT_SVN = 1
     TYPE_GIT_P4 = 2
diff --git a/rbtools/clients/gitrepositoryhooks.py b/rbtools/clients/gitrepositoryhooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..9390120aac1782b5138b3c779584414140b0f976
--- /dev/null
+++ b/rbtools/clients/gitrepositoryhooks.py
@@ -0,0 +1,152 @@
+from __future__ import unicode_literals
+
+import six
+
+from collections import defaultdict
+from copy import deepcopy
+
+from rbtools.clients.scmrepositoryhooks import SCMRepositoryHooks
+
+
+class GitRepositoryHooks(SCMRepositoryHooks):
+    """Repository hook functionality for Git repositories.
+
+    A representation of the Git specific tools needed for RBTools repo-hook
+    command functionality.
+    """
+
+    def __init__(self):
+        """Initialize class"""
+        pass
+
+    def pre_receive(self, lines, regex):
+        """Returns the data needed for rbt repo-hook pre-receive hook script.
+
+        Args:
+            lines (list)
+                The newrev, oldrev, and refname for the current push
+
+            regex (Regular Expression Object)
+                The regex pattern used to pull review request IDs from commit
+                messages.
+        """
+        return self.get_review_id_to_commits_map(lines, regex)
+
+    def post_receive(self, lines, regex):
+        """Returns the data needed for rbt repo-hook post-receive hook script.
+
+        Args:
+            lines (list)
+                The newrev, oldrev, and refname for the current push
+
+            regex (Regular Expression Object)
+                The regex pattern used to pull review request IDs from commit
+                messages.
+        """
+        return self.get_review_id_to_commits_map(lines, regex)
+
+    def get_branch_name(self, ref_name):
+        """Returns the branch name corresponding to the specified ref name."""
+        branch_ref_prefix = 'refs/heads/'
+
+        if ref_name.startswith(branch_ref_prefix):
+            return ref_name[len(branch_ref_prefix):]
+
+    def get_commit_hashes(self, old_rev, new_rev):
+        """Returns a list of abbreviated commit hashes from old_rev to new_rev.
+        """
+        git_command = ['git', 'rev-list', '--abbrev-commit', '--reverse',
+                       '%s..%s' % (old_rev, new_rev)]
+        return self.execute(git_command).split('\n')
+
+    def get_unique_commit_hashes(self, ref_name, new_rev):
+        """Returns a list of abbreviated commit hashes unique to ref_name."""
+        git_command = ['git', 'rev-list', new_rev, '--abbrev-commit',
+                       '--reverse', '--not']
+        git_command.extend(self.get_excluded_branches(ref_name))
+        return self.execute(git_command).strip().split('\n')
+
+    def get_excluded_branches(self, ref_name):
+        """Returns a list of all branches, excluding the specified branch."""
+        git_command = ['git', 'for-each-ref', 'refs/heads/',
+                       '--format=%(refname)']
+        all_branches = self.execute(git_command).strip().split('\n')
+        return [branch.strip() for branch in
+                all_branches if branch != ref_name]
+
+    def get_branches_containing_commit(self, commit_hash):
+        """Returns a list of all branches containing the specified commit."""
+        git_command = ['git', 'branch', '--contains', commit_hash]
+        branches = self.execute(git_command).replace('*', '').split('\n')
+        return [branch.strip() for branch in branches]
+
+    def get_commit_message(self, commit):
+        """Returns the specified commit's commit message."""
+        git_command = ['git', 'show', '-s', '--pretty=format:%B', commit]
+        return self.execute(git_command).strip()
+
+    def get_review_id_to_commits_map(self, lines, regex):
+        """
+        Returns a dictionary, mapping a review request ID to a list
+        of commits.
+
+        The commits must be in the form: oldrev newrev refname (separated by
+        newlines), as given by a Git pre-receive or post-receive hook.
+
+        If a commit's commit message does not contain a review request ID, we
+        append the commit to the key 0.
+        """
+        review_id_to_commits_map = defaultdict(list)
+
+        # Store a list of new branches (which have an all-zero old_rev value)
+        # created in this push to handle them specially.
+        new_branches = []
+        null_sha1 = '0' * 40
+
+        for line in lines:
+            old_rev, new_rev, ref_name = line.split()
+            branch_name = self.get_branch_name(ref_name)
+
+            if not branch_name or new_rev == null_sha1:
+                continue
+
+            if old_rev == null_sha1:
+                new_branches.append(branch_name)
+                commit_hashes = self.get_unique_commit_hashes(ref_name,
+                                                              new_rev)
+            else:
+                commit_hashes = self.get_commit_hashes(old_rev, new_rev)
+
+            for commit_hash in commit_hashes:
+                if commit_hash:
+                    commit_message = self.get_commit_message(commit_hash)
+                    review_request_id = \
+                        self.get_review_request_id(regex, commit_message)
+
+                    commit = '%s (%s)' % (branch_name, commit_hash)
+                    review_id_to_commits_map[review_request_id].append(commit)
+
+        # If there are new branches, check every commit in the dictionary
+        # (corresponding to only old branches) to see if the new branches also
+        # contain that commit.
+        if new_branches:
+            review_id_to_commits_map_copy = deepcopy(review_id_to_commits_map)
+
+            for review_id, commit_list in six.iteritems(
+                    review_id_to_commits_map_copy):
+                for commit in commit_list:
+                    commit_branch = commit[:commit.find('(') - 1]
+
+                    if commit_branch in new_branches:
+                        continue
+
+                    commit_hash = commit[commit.find('(') + 1:-1]
+                    commit_branches = \
+                        self.get_branches_containing_commit(commit_hash)
+
+                    for branch in \
+                        set(new_branches).intersection(commit_branches):
+                        new_commit = '%s (%s)' % (branch, commit_hash)
+                        review_id_to_commits_map[review_id].append(new_commit)
+
+        return review_id_to_commits_map
diff --git a/rbtools/clients/mercurial.py b/rbtools/clients/mercurial.py
index a95561d4d64861f8b702bdd31a53d6d62fe1ea90..571e87d7eaa0a1d36fd847ac99159ca4058c7803 100644
--- a/rbtools/clients/mercurial.py
+++ b/rbtools/clients/mercurial.py
@@ -16,6 +16,7 @@ from rbtools.clients.errors import (CreateCommitError,
                                     MergeError,
                                     SCMError,
                                     TooManyRevisionsError)
+from rbtools.clients.mercurialrepositoryhooks import MercurialRepositoryHooks
 from rbtools.clients.svn import SVNClient
 from rbtools.utils.checks import check_install
 from rbtools.utils.console import edit_file
@@ -58,6 +59,8 @@ class MercurialClient(SCMClient):
     can_branch = True
     can_merge = True
 
+    repository_hooks_class = MercurialRepositoryHooks
+
     PRE_CREATION = '/dev/null'
     PRE_CREATION_DATE = 'Thu Jan 01 00:00:00 1970 +0000'
     NO_PARENT = '0' * 40
diff --git a/rbtools/clients/mercurialrepositoryhooks.py b/rbtools/clients/mercurialrepositoryhooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..10b91b0bd664cf3e69089de81c786bfce7eedce1
--- /dev/null
+++ b/rbtools/clients/mercurialrepositoryhooks.py
@@ -0,0 +1,23 @@
+from rbtools.clients.scmrepositoryhooks import SCMRepositoryHooks
+
+
+class MercurialRepositoryHooks(SCMRepositoryHooks):
+    """Repository hook functionality for Mercurial repositories.
+
+    A representation of the Git specific tools needed for RBTools repo-hook
+    command functionality.
+    """
+
+    def __init__(self):
+        """Initialize class"""
+        pass
+
+    def pre_receive():
+        """Returns the data needed for rbt repo-hook post-receive hook script.
+        """
+        pass
+
+    def post_receive():
+        """Returns the data needed for rbt repo-hook post-receive hook script.
+        """
+        pass
diff --git a/rbtools/clients/scmrepositoryhooks.py b/rbtools/clients/scmrepositoryhooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5bbdaad375c8ab0d4cd81f78dccbe6c33b085f0
--- /dev/null
+++ b/rbtools/clients/scmrepositoryhooks.py
@@ -0,0 +1,48 @@
+import logging
+import subprocess
+
+
+class SCMRepositoryHooks(object):
+    """General repository hook functionality.
+
+    A representation of the SCM specific tools needed for RBTools repo-hook
+    command functionality.
+    """
+
+    def __init__(self):
+        """Initialize the class"""
+        pass
+
+    def pre_receive():
+        """Returns the data needed for rbt repo-hook pre-receive hook script.
+
+        This method will be overwritten by SCM specific subclasses.
+        """
+        pass
+
+    def post_receive():
+        """Returns the data needed for rbt repo-hook pre-receive hook script.
+
+        This method will be overwritten by SCM specific subclasses.
+        """
+        pass
+
+    def execute(self, command):
+        """Executes the specified command and returns the stdout output."""
+        process = subprocess.Popen(command, stdout=subprocess.PIPE)
+        output = process.communicate()[0].strip()
+
+        if process.returncode:
+            logging.warning('Failed to execute command: %s', command)
+            return None
+
+        return output
+
+    def get_review_request_id(self, regex, commit_message):
+        """Returns the review request ID referenced in the commit message.
+
+        We assume there is at most one review request associated with each
+        commit. If a matching review request cannot be found, we return 0.
+        """
+        match = regex.search(commit_message)
+        return (match and int(match.group('id'))) or 0
diff --git a/rbtools/clients/svn.py b/rbtools/clients/svn.py
index 070157fb74197170a81a4895120cc5f141a74570..75ed7c5f8035122259636d9ceec589115108ff0a 100644
--- a/rbtools/clients/svn.py
+++ b/rbtools/clients/svn.py
@@ -19,6 +19,7 @@ from rbtools.clients.errors import (AuthenticationError,
                                     InvalidRevisionSpecError,
                                     MinimumVersionError, OptionsCheckError,
                                     SCMError, TooManyRevisionsError)
+from rbtools.clients.svnrepositoryhooks import SVNRepositoryHooks
 from rbtools.utils.checks import (check_gnu_diff, check_install,
                                   is_valid_version)
 from rbtools.utils.console import get_pass
@@ -43,6 +44,8 @@ class SVNClient(SCMClient):
     supports_diff_exclude_patterns = True
     supports_patch_revert = True
 
+    repository_hooks_class = SVNRepositoryHooks
+
     INDEX_SEP = b'=' * 67
     INDEX_FILE_RE = re.compile(b'^Index: (.+?)(?:\t\((added|deleted)\))?\n$')
 
diff --git a/rbtools/clients/svnrepositoryhooks.py b/rbtools/clients/svnrepositoryhooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8bc1fb02ee64def252d535d215f0a2640754a01
--- /dev/null
+++ b/rbtools/clients/svnrepositoryhooks.py
@@ -0,0 +1,23 @@
+from rbtools.clients.scmrepositoryhooks import SCMRepositoryHooks
+
+
+class SVNRepositoryHooks(SCMRepositoryHooks):
+    """Repository hook functionality for Subversion repositories.
+
+    A representation of the Subversion specific tools needed for RBTools
+    repo-hook command functionality.
+    """
+
+    def __init__(self):
+        """Initialize the class."""
+        pass
+
+    def pre_receive():
+        """Returns the data needed for rbt repo-hook pre-receive hook script.
+        """
+        pass
+
+    def post_receive():
+        """Returns the data needed for rbt repo-hook pre-receive hook script.
+        """
+        pass
diff --git a/rbtools/commands/repo_hook.py b/rbtools/commands/repo_hook.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c26fe97bbe61999a6e113d0ee4059fa0a70f46d
--- /dev/null
+++ b/rbtools/commands/repo_hook.py
@@ -0,0 +1,388 @@
+from __future__ import print_function, unicode_literals
+
+import logging
+import re
+import sys
+
+from rbtools.api.client import RBClient
+from rbtools.api.errors import APIError, ServerInterfaceError
+from rbtools.clients.git import GitClient
+from rbtools.commands import Command, Option
+
+#: A string used in the close_review_request method.
+#: This is based off of a similar constant found in rbtools/hooks/common.py
+SUBMITTED = 'submitted'
+
+
+class HookError(Exception):
+    pass
+
+
+class RepoHook(Command):
+    """Provide hook script functionlity for RBTools"""
+
+    #: The regex flags that will be used for parsing review request ids from
+    #: commit messages.
+    #: Changes depending on options passed.
+    regex_flags = None
+
+    #: The subclass of rbtools.clients.scmrepositoryhooks.SCMRepositoryHooks
+    #: that will be used for hook functionality.
+    #: Changes depending on the --repo-type option passed.
+    repo_hook_class = None
+
+    name = 'repo-hook'
+    author = 'The Review Board Project'
+    description = ('Provide a standardized method for how hooks are '
+                   'used by scripts')
+    args = ('--hook-type=pre-receive --require-approval '
+            '--hook-arguments=... --api-token=...')
+    option_list = [
+        Option('--hook-type',
+               dest='hook_type',
+               default=None,
+               help='What kind of hook will be used. The possible kinds of '
+                    'hooks include pre-receive and post-receive. '
+                    'The pre-receive hook will check if each review request '
+                    'corresponding to a commit has been approved. The '
+                    'post-receive hook will automatically close review '
+                    'requests as "submitted" after a push.'),
+        Option('--repo-type',
+               dest='repo_type',
+               default=None,
+               help='What SCM system the repository belongs to. '
+                    'Currently only accepts \'git\' (Github) as a valid'
+                    'option'),
+        Option('--require-approval',
+               action='store_true',
+               dest='require_approval',
+               default=False,
+               help='Whether this hook requires approval. Defaults to False'),
+        Option('--reviewboard-url',
+               dest='reviewboard_url',
+               default='http://reviewboard.example.com',
+               help='The Review Board server URL to be used. '
+                    'Defaults to \'http://reviewboard.example.com\'.'),
+        Option('--reviewboard-username',
+               dest='reviewboard_username',
+               default='special_user',
+               help='The username to be supplied to the Review Board server. '
+                    'Defaults to \'special_user\'.'),
+        Option('--reviewboard-password',
+               dest='reviewboard_password',
+               default='password',
+               help='The password to be supplied to the Review Board server. '
+                    'Defaults to \'password\'.'),
+        Option('--regex',
+               dest='regex',
+               default=None,
+               help='The regular expression used to match review request '
+                    'IDs. Defaults to r\'(?:Reviewed at %%s/r/|Review request '
+                    '#)(?P<id>\\d+)\' %% reviewboard_url.'),
+        Option('--regex-acknowledge-case',
+               dest='regex_acknowledge_case',
+               action='store_true',
+               default=False,
+               help='Whether the IGNORECASE flag should be turned off for '
+                    'the regular expression. Defaults to False.'),
+        Option('--dont-require-review-requests',
+               dest='dont_require_review_requests',
+               action='store_true',
+               default=False,
+               help='Whether it is not required that every single commit '
+                    'references a review request. Defaults to False. Used '
+                    'for the post-recieve repository hook'),
+        Option('--allow-unapproved-push',
+               dest='allow_unapproved_push',
+               action='store_true',
+               default=False,
+               help='Whether a push will be allowed if it contains any '
+                    'unapproved commits. Defaults to False. Used for the '
+                    'pre-recieve repository hook'),
+        Command.server_options,
+        Command.diff_options,
+        Command.branch_options,
+        Command.repository_options,
+        Command.git_options,
+        Command.perforce_options,
+        Command.subversion_options,
+        Command.tfs_options,
+    ]
+
+    def main(self):
+        """Perform repository hook script functionality for RBTools."""
+        self.initialize_logging()
+
+        self.set_regex_flags()
+
+        # Determine if a Regex pattern was given, and set it accordingly
+        if(self.options.regex is None):  # no custom REGEX was input.
+            self.options.regex = r'(?:Reviewed at %s/r/|Review request #)'\
+                                 '(?P<id>\\d+)' % self.options.reviewboard_url
+
+        # Setting self.repo_hook_class based on --repo-type
+        self.set_repository_hooks_class()
+
+        # Determining what hook type is being requested, and calling the
+        # appropriate method. Warnings will be given if required.
+        self.call_hook_method()
+
+        # Successfully exit the command
+        return
+
+    def pre_receive(self):
+        """Check commits in push for review request ids.
+
+        Check each commit in the current push for whether is contains a
+        review request number as per the regex expression in
+        self.options.regex.
+
+        This method's functionality depends on two options:
+        dont-require-review-requests and allow-unapproved-push, which both
+        default to False.
+
+        If dont-require-review-requests is left as the default value, the
+        method will mark the push as unapproved if there are any commits
+        without a review request in the commit mesasge. If
+        dont-require-review-requests is given as an option, the method will
+        not mark the push as unapproved in the case of commits without a
+        review request in the commit message.
+
+        If the allow-unapproved-push is left on the default value, the
+        method will not allow a push to progress if it has been marked as
+        unapproved, and will raise a HookError.
+        If allow-unapproved-push is passed as an option, the method will
+        allow a push to go through, even if it has been marked as
+        unnapproved.
+
+        Raises:
+            rbtools.commands.repo_hook.HookError:
+                An error occured while performing hook functionality.
+        """
+        # HookError will be raised if the push is being declined due to
+        #   unapproved commits.
+        lines = sys.stdin.readlines()
+        compiled_regex = re.compile(self.options.regex, self.regex_flags)
+
+        review_id_to_commits = \
+            self.repo_hook_class.pre_receive(lines, compiled_regex)
+
+        is_push_approved = True
+
+        # Check if each review request in the dictionary is approved.
+        for review_request_id in review_id_to_commits:
+            if review_request_id:
+                is_approved, approval_failure = \
+                    self.get_review_request_approval(
+                        self.options.reviewboard_url,
+                        self.options.reviewboard_username,
+                        self.options.reviewboard_password,
+                        review_request_id)
+
+                if not is_approved:
+                    is_push_approved = False
+                    commits = \
+                        ', '.join(review_id_to_commits[review_request_id])
+                    logging.warning('Review request #%s for %s is not '
+                                    'approved: %s', review_request_id, commits,
+                                    approval_failure)
+            elif not review_request_id and \
+                not self.options.dont_require_review_requests:
+                is_push_approved = False
+                commits = ', '.join(review_id_to_commits[review_request_id])
+                logging.warning('A review request has not been created for %s',
+                                commits)
+
+        if not self.options.allow_unapproved_push and not is_push_approved:
+            logging.warning('Declining the push - there are 1 or more '
+                            'unapproved commits.')
+            raise HookError('Declining the push - there are 1 or  more'
+                            'unnaproved commits.')
+
+        return
+
+    def post_receive(self):
+        '''Close review requests associated with commits in the push.'''
+        lines = sys.stdin.readlines()
+        compiled_regex = re.compile(self.options.regex, self.regex_flags)
+
+        review_id_to_commits = \
+            self.repo_hook_class.post_receive(lines, compiled_regex)
+
+        # Close each review request in the dictionary as submitted.
+        for review_request_id in review_id_to_commits:
+            if not review_request_id:
+                logging.warning('No matching review request ID found for '
+                                'commits: '
+                                + ', '.join(
+                                  review_id_to_commits[review_request_id]))
+                continue
+
+            description = ('Pushed to '
+                           + ', '.join(
+                             review_id_to_commits[review_request_id]))
+            self.close_review_request(self.options.reviewboard_url,
+                                      self.options.reviewboard_username,
+                                      self.options.reviewboard_password,
+                                      review_request_id, description)
+
+        return
+
+    def set_repository_hooks_class(self):
+        '''Return the needed SCMRepositoryHooks subclass.
+
+        Raises:
+            rbtools.commands.repo_hook.HookError:
+                An error occured while performing hook functionality.
+        '''
+        # HookError will be raised if an invalid hook-type is given.
+        if(self.options.repo_type == "git"):
+            self.repo_hook_class = GitClient.repository_hooks_class()
+
+        elif(self.options.repo_type == "svn"):
+            logging.warning('Subversion is not currently supported for '
+                            'RBTools repo-hook command.')
+            raise HookError('Subversion is not currently supported for the '
+                            'RepoHook command')
+            # self.repo_hook_class = SVNClient.repository_hooks_class()
+            # Once SVN functionality for the repo-hook command is implemented,
+            #   uncomment the above code.
+
+        elif(self.options.repo_type == "mercurial"):
+            logging.warning('Mercurial is not currently supported for '
+                            'RBTools repo-hook command.')
+            raise HookError('Mercurial is not currently supported for the '
+                            'RepoHook command')
+            # self.repo_hook_class = MercurialClient.repository_hooks_class()
+            # Once Mercurial functionality for the repo-hook command is
+            #   implemented, uncomment the above code.
+
+        else:
+            logging.warning('Invalid repo-type entered. Please enter a valid '
+                            'repo-type and try again')
+            raise HookError('Invalid repo-type entered. Please enter a valid '
+                            'repo-type and try again')
+
+    def call_hook_method(self):
+        """Call method for correct repo hook functionality
+
+        Raises:
+            rbtools.commands.repo_hook.HookError:
+                An error occured while performing hook functionality.
+        """
+        # HookError will be raised if an invalid hook-type is given.
+        if(self.options.hook_type == "pre-receive"):
+            self.pre_receive()
+        elif(self.options.hook_type == "post-receive"):
+            self.post_receive()
+        else:
+            logging.warning('Invalid hook-type entered. Please enter a valid '
+                            'hook-type and try again')
+            raise HookError('Invalid hook-type entered. Please enter a valid '
+                            'repo-type and try again')
+
+    def get_api(self, server_url, **kwargs):
+        """Returns an RBClient instance and the associated root resource.
+
+        Hooks should use this method to gain access to the API, instead of
+        instantiating their own client.
+
+        Args:
+            server_url (unicode):
+                The server URL to retrieve.
+
+            **kwargs (dict):
+                Additional keyword arguments to pass to the
+                :py:class:`~rbtools.api.client.RBClient` constructor. See
+                :py:meth:`SyncTransport.__init__()
+                <rbtools.api.transport.sync.SyncTransport.__init__>` for
+                arguments that are accepted.
+
+        Returns:
+            tuple:
+            This returns a 2-tuple of the
+            :py:class:`~rbtools.api.client.RBClient` and
+            :py:class:`<root resource> rbtools.api.resource.Resource`.
+
+        Raises:
+            rbtools.commands.repo_hook.HookError:
+                An error occured while performing hook functionality.
+        """
+        # HookError will be raised if the Review Board server is not reachable,
+        #   or if there is an unexpected API error encountered.
+        api_client = RBClient(server_url, **kwargs)
+
+        try:
+            api_root = api_client.get_root()
+        except ServerInterfaceError as e:
+            raise HookError('Could not reach the Review Board server at %s: %s'
+                            % (server_url, e))
+        except APIError as e:
+            raise HookError('Unexpected API Error: %s' % e)
+
+        return api_client, api_root
+
+    def initialize_logging(self):
+        """Sets up a log handler to format log messages.
+
+        Warning, error, and critical messages will show the level name as
+        a prefix, followed by the message.
+        """
+        root = logging.getLogger()
+
+        handler = logging.StreamHandler()
+        handler.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))
+        handler.setLevel(logging.WARNING)
+        root.addHandler(handler)
+
+    def get_review_request(self, review_request_id, api_root):
+        """Returns the review request resource for the given ID.
+
+        Raises:
+            rbtools.commands.repo_hook.HookError:
+                An error occured while performing hook functionality.
+        """
+        # HookError will be raised if there is an error getting a review
+        #   request
+        try:
+            review_request = api_root.get_review_request(
+                review_request_id=review_request_id)
+        except APIError as e:
+            raise HookError('Error getting review request: %s' % e)
+
+        return review_request
+
+    def close_review_request(self, server_url, username, password,
+                             review_request_id, description):
+        """Closes the specified review request as submitted."""
+        api_client, api_root = self.get_api(server_url, username=username,
+                                            password=password)
+        review_request = self.get_review_request(review_request_id, api_root)
+
+        if review_request.status == SUBMITTED:
+            logging.warning('Review request #%s is already %s.',
+                            review_request_id, SUBMITTED)
+            return
+
+        if description:
+            review_request = review_request.update(status=SUBMITTED,
+                                                   description=description)
+        else:
+            review_request = review_request.update(status=SUBMITTED)
+
+        print('Review request #%s is set to %s.' %
+              (review_request_id, review_request.status))
+
+    def get_review_request_approval(self, server_url, username, password,
+                                    review_request_id):
+        """Returns the approval information for the given review request."""
+        api_client, api_root = self.get_api(server_url, username=username,
+                                            password=password)
+        review_request = self.get_review_request(review_request_id, api_root)
+
+        return review_request.approved, review_request.approval_failure
+
+    def set_regex_flags(self):
+        """Set the Regex flags for regex expression."""
+        if(not self.options.regex_acknowledge_case):
+            self.regex_flags = re.IGNORECASE
diff --git a/rbtools/commands/tests/test_repo_hook.py b/rbtools/commands/tests/test_repo_hook.py
new file mode 100644
index 0000000000000000000000000000000000000000..7458249110e67e480586793ee221cb8f89e4c8df
--- /dev/null
+++ b/rbtools/commands/tests/test_repo_hook.py
@@ -0,0 +1,253 @@
+"""Test for the RBTools repo-hook command"""
+
+from __future__ import unicode_literals
+
+import re
+
+from kgb import SpyAgency
+
+from rbtools.commands.repo_hook import RepoHook, HookError
+from rbtools.utils.testbase import RBTestBase
+from rbtools.clients.gitrepositoryhooks import GitRepositoryHooks
+
+
+class RepoHookCommandTests(SpyAgency, RBTestBase):
+    """Tests for rbt repo-hook command"""
+
+    def test_git_repo_type(self):
+        """Testing repo_hook properly sets repo_hook_class to be a
+        GitRepositoryHooks class, when it it given git as the repo-type
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--repo-type", "git",
+        ])
+
+        repo_hook.set_repository_hooks_class()
+
+        self.assertEqual(type(repo_hook.repo_hook_class),
+                         type(GitRepositoryHooks()))
+
+    def test_svn_repo_type(self):
+        """Testing repo_hook raises a HookError when it it given 'svn' as
+        the repo-type
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--repo-type", "svn",
+        ])
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_mercurial_repo_type(self):
+        """Testing repo_hook raises a HookError when it it given 'mercurial'
+        as the repo-type
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--repo-type", "mercurial",
+        ])
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_invalid_repo_type(self):
+        """Testing repo_hook raises a HookError when it it given an invalid
+        repo-type
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--repo-type", "invalid-repo-type",
+        ])
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_default_repo_type(self):
+        """Testing repo_hook raises a HookError when it is not given a
+        repo-type
+        """
+        repo_hook = self._create_repo_hook_command()
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_pre_receive_hook_type(self):
+        """Testing repo_hook when given --hook-type pre-receive"""
+        repo_hook = self._create_repo_hook_command(args=[
+            "--hook-type", "pre-receive",
+        ])
+
+        self.spy_on(RepoHook.pre_receive, call_original=False,
+                    owner=RepoHook)
+
+        repo_hook.call_hook_method()
+
+        self.assertSpyCalled(RepoHook.pre_receive)
+
+        RepoHook.pre_receive.unspy()
+
+    def test_post_receive_hook_type(self):
+        """Testing repo_hook when given --hook-type post-receive"""
+        repo_hook = self._create_repo_hook_command(args=[
+            "--hook-type", "post-receive"
+        ])
+
+        self.spy_on(RepoHook.post_receive, call_original=False,
+                    owner=RepoHook)
+
+        repo_hook.call_hook_method()
+
+        self.assertSpyCalled(RepoHook.post_receive)
+
+        RepoHook.post_receive.unspy()
+
+    def test_invalid_hook_type(self):
+        """Testing repo_hook properly raises a HookError if given an invalid
+        hook-type
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--hook-type", "invalid_hook_type",
+        ])
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_default_hook_type(self):
+        """Testing repo_hook properly exits if not given a hook-type"""
+        repo_hook = self._create_repo_hook_command()
+
+        with self.assertRaises(HookError):
+            repo_hook.set_repository_hooks_class()
+
+    def test_server_options_specified(self):
+        """Testing repo_hook properly saves valid server options"""
+        repo_hook = self._create_repo_hook_command(args=[
+            "--reviewboard-url",      "test_url",
+            "--reviewboard-username", "test_username",
+            "--reviewboard-password", "test_password",
+        ])
+
+        options = repo_hook.options
+
+        self.assertEqual(options.reviewboard_url,      "test_url")
+        self.assertEqual(options.reviewboard_username, "test_username")
+        self.assertEqual(options.reviewboard_password, "test_password")
+
+    def test_server_options_default(self):
+        """Testing repo_hook has proper default values"""
+        repo_hook = self._create_repo_hook_command()
+
+        options = repo_hook.options
+
+        self.assertEqual(options.reviewboard_url,
+                         "http://reviewboard.example.com")
+        self.assertEqual(options.reviewboard_username, "special_user")
+        self.assertEqual(options.reviewboard_password, "password")
+
+    def test_regex_ignore_case(self):
+        """Testing repo_hook properly assigns the regex_flags with default
+        value
+        """
+        repo_hook = self._create_repo_hook_command()
+
+        repo_hook.set_regex_flags()
+
+        self.assertEqual(repo_hook.regex_flags, re.IGNORECASE)
+        self.assertEqual(repo_hook.options.regex_acknowledge_case, False)
+
+    def test_regex_acknowledge_case(self):
+        """Testing repo_hook properly assigns the regex_flags with calling
+        regex_acknowledge_case
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--regex-acknowledge-case"
+        ])
+
+        repo_hook.set_regex_flags()
+
+        self.assertEqual(repo_hook.regex_flags, None)
+        self.assertEqual(repo_hook.options.regex_acknowledge_case, True)
+
+    def test_require_approval_default(self):
+        """Testing repo_hook has correct default value for require_approval
+        option
+        """
+        repo_hook = self._create_repo_hook_command()
+
+        self.assertEqual(repo_hook.options.require_approval, False)
+
+    def test_require_approval_activated(self):
+        """Testing repo_hook has correct default value for require_approval
+        option
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--require-approval",
+        ])
+
+        self.assertEqual(repo_hook.options.require_approval, True)
+
+    def test_dont_require_review_request_default(self):
+        """Testing repo_hook has correct default value for the
+        --dont-require-review-request option option
+        """
+        repo_hook = self._create_repo_hook_command()
+
+        self.assertEqual(repo_hook.options.dont_require_review_requests, False)
+
+    def test_dont_require_review_request_activated(self):
+        """Testing repo_hook has correct value when the
+        --dont-require-review-request option is activated
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--dont-require-review-requests",
+        ])
+
+        self.assertEqual(repo_hook.options.dont_require_review_requests, True)
+
+    def test_allow_unapproved_push_default(self):
+        """Testing repo_hook has correct default value for
+        --allow-unapproved-push option
+        """
+        repo_hook = self._create_repo_hook_command()
+
+        self.assertEqual(repo_hook.options.allow_unapproved_push, False)
+
+    def test_allow_unapproved_push_activated(self):
+        """Testing repo_hook has correct value for when the
+        --allow-unapproved-push option is activated
+        """
+        repo_hook = self._create_repo_hook_command(args=[
+            "--allow-unapproved-push",
+        ])
+
+        self.assertEqual(repo_hook.options.allow_unapproved_push, True)
+
+    def _create_repo_hook_command(self, fields=None, args=None):
+        """Create an argument parser with the given extra fields.
+
+        Args:
+            fields (list of unicode):
+                A list of key-value pairs for the field argument.
+
+                Each pair should be of the form key=value.
+
+            args (list of unicode):
+                A list of command line arguments to be passed to the parser.
+
+                The command line will receive each item in the list.
+
+        Returns:
+            rbtools.commands.post.POST:
+            A POST instance for communicating with the rbt server
+        """
+        repo_hook = RepoHook()
+        argv = ['rbt', 'repo-hook']
+
+        if args is not None:
+            argv.extend(args)
+
+        parser = repo_hook.create_arg_parser(argv)
+        repo_hook.options = parser.parse_args(argv[2:])
+
+        if fields is not None:
+            repo_hook.options.fields = fields
+
+        return repo_hook
diff --git a/setup.py b/setup.py
index fbff0add17f6bf4b212dd12c3228730baa0cd9d4..ac993334d43b7b1f653a6426139652f43ff2a750 100755
--- a/setup.py
+++ b/setup.py
@@ -76,6 +76,7 @@ rb_commands = [
     'patch = rbtools.commands.patch:Patch',
     'post = rbtools.commands.post:Post',
     'publish = rbtools.commands.publish:Publish',
+    'repo-hook = rbtools.commands.repo_hook:RepoHook',
     'setup-completion = rbtools.commands.setup_completion:SetupCompletion',
     'setup-repo = rbtools.commands.setup_repo:SetupRepo',
     'stamp = rbtools.commands.stamp:Stamp',
