diff --git a/src/centpkg.conf b/src/centpkg.conf
index 79a16ba..24092cf 100644
--- a/src/centpkg.conf
+++ b/src/centpkg.conf
@@ -26,3 +26,7 @@ git_excludes =
   /.build-*.log
   results_*/
   clog
+
+[centpkg.distgit]
+apibaseurl = https://gitlab.com
+token = 
diff --git a/src/centpkg/cli.py b/src/centpkg/cli.py
index 2bd69d7..798ba11 100755
--- a/src/centpkg/cli.py
+++ b/src/centpkg/cli.py
@@ -14,7 +14,13 @@
 # the full text of the license.
 
 from __future__ import print_function
+
+import argparse
+import textwrap
+
+from centpkg.utils import config_get_safely, do_add_remote, do_fork
 from pyrpkg.cli import cliClient
+from six.moves.urllib_parse import urlparse
 
 
 class centpkgClient(cliClient):
@@ -25,10 +31,89 @@ class centpkgClient(cliClient):
         self.setup_centos_subparsers()
 
     def setup_centos_subparsers(self):
-        self.register_parser()
+        self.register_do_fork()
 
-    def register_parser(self):
-        pass
+    def register_do_fork(self):
+        help_msg = 'Create a new fork of the current repository'
+        distgit_section = '{0}.distgit'.format(self.name)
+        distgit_api_base_url = config_get_safely(self.config, distgit_section, "apibaseurl")
+        description = textwrap.dedent('''
+            Create a new fork of the current repository
+
+            Before the operation, you need to generate an API token at
+            https://{1}/-/profile/personal_access_tokens, select the relevant
+            scope(s) and save it in your local user configuration located
+            at ~/.config/rpkg/{0}.conf. For example:
+
+                [{0}.distgit]
+                token = <api_key_here>
+
+            Below is a basic example of the command to fork a current repository:
+
+                {0} fork
+
+            Operation requires username (GITLAB_ID). by default, current logged
+            username is taken. It could be overridden by reusing an argument:
+
+                {0} --user GITLAB_ID fork
+        '''.format(self.name, urlparse(distgit_api_base_url).netloc))
+
+        fork_parser = self.subparsers.add_parser(
+            'fork',
+            formatter_class=argparse.RawDescriptionHelpFormatter,
+            help=help_msg,
+            description=description)
+        fork_parser.set_defaults(command=self.do_distgit_fork)
+
+    def do_distgit_fork(self):
+        """create fork of the distgit repository
+        That includes creating fork itself using API call and then adding
+        remote tracked repository
+        """
+        distgit_section = '{0}.distgit'.format(self.name)
+        distgit_api_base_url = config_get_safely(self.config, distgit_section, "apibaseurl")
+        distgit_remote_base_url = self.config.get(
+            '{0}'.format(self.name),
+            "gitbaseurl",
+            vars={'user': self.cmd.user, 'repo': self.cmd.repo_name},
+        )
+        distgit_token = config_get_safely(self.config, distgit_section, 'token')
+
+        ret = do_fork(
+            logger=self.log,
+            base_url=distgit_api_base_url,
+            token=distgit_token,
+            repo_name=self.cmd.repo_name,
+            namespace=self.cmd.ns,
+            cli_name=self.name,
+        )
+
+        # assemble url of the repo in web browser
+        fork_url = '{0}/{1}/{2}'.format(
+            distgit_api_base_url.rstrip('/'),
+            self.cmd.user,
+            self.cmd.repo_name,
+        )
+
+        if ret:
+            msg = "Fork of the repository has been created: '{0}'"
+        else:
+            msg = "Repo '{0}' already exists."
+        self.log.info(msg.format(fork_url))
+
+        ret = do_add_remote(
+            base_url=distgit_api_base_url,
+            remote_base_url=distgit_remote_base_url,
+            username=self.cmd.user,
+            repo=self.cmd.repo,
+            repo_name=self.cmd.repo_name,
+            namespace=self.cmd.ns,
+        )
+        if ret:
+            msg = "Adding as remote '{0}'."
+        else:
+            msg = "Remote with name '{0}' already exists."
+        self.log.info(msg.format(self.cmd.user))
 
 
 class centpkgClientSig(cliClient):
diff --git a/src/centpkg/utils.py b/src/centpkg/utils.py
new file mode 100644
index 0000000..634e8bb
--- /dev/null
+++ b/src/centpkg/utils.py
@@ -0,0 +1,146 @@
+# -*- coding: utf-8 -*-
+# utils.py - a module with support methods for centpkg
+#
+# Copyright (C) 2021 Red Hat Inc.
+# Author(s): Ondrej Nosek <onosek@redhat.com>
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
+# the full text of the license.
+
+import json
+
+import git
+import requests
+from pyrpkg import rpkgError
+from requests.exceptions import ConnectionError
+from six.moves.configparser import NoOptionError, NoSectionError
+from six.moves.urllib.parse import quote_plus, urlparse
+
+
+def do_fork(logger, base_url, token, repo_name, namespace, cli_name):
+    """
+    Creates a fork of the project.
+    :param logger: A logger object
+    :param base_url: a string of the URL repository
+    :param token: a string of the API token that has rights to make a fork
+    :param repo_name: a string of the repository name
+    :param namespace: a string determines a type of the repository
+    :param cli_name: string of the CLI's name (e.g. centpkg)
+    :return: a bool; True when fork was created, False when already exists
+    """
+    api_url = '{0}/api/v4'.format(base_url.rstrip('/'))
+    project_id = quote_plus("redhat/centos-stream/{0}/{1}".format(namespace, repo_name))
+    fork_url = '{0}/projects/{1}/fork'.format(api_url, project_id)
+
+    headers = {
+        'PRIVATE-TOKEN': token,
+        'Accept': 'application/json',
+        'Content-Type': 'application/json'
+    }
+    payload = json.dumps({})
+    try:
+        rv = requests.post(
+            fork_url, headers=headers, data=payload, timeout=60)
+    except ConnectionError as error:
+        error_msg = ('The connection to API failed while trying to '
+                     'create a new fork. The error was: {0}'.format(str(error)))
+        raise rpkgError(error_msg)
+
+    try:
+        # Extract response json for debugging
+        rv_json = rv.json()
+        logger.debug("Pagure API response: '{0}'".format(rv_json))
+    except Exception:
+        pass
+
+    base_error_msg = ('The following error occurred while creating a new fork: {0}')
+    if not rv.ok:
+        # fork was already created
+        if rv.status_code == 409 or rv.reason == "Conflict":
+            return False
+        # show hint for invalid, expired or revoked token
+        elif rv.status_code == 401 or rv.reason == "Unauthorized":
+            base_error_msg += '\nFor invalid or expired token refer to ' \
+                '"{0} fork -h" to set a token in your user ' \
+                'configuration.'.format(cli_name)
+        raise rpkgError(base_error_msg.format(rv.text))
+
+    return True
+
+
+def do_add_remote(base_url, remote_base_url, username, repo, repo_name,
+                  namespace):
+    """
+    Adds remote tracked repository
+    :param base_url: a string of the URL repository
+    :param remote_base_url: a string of the remote tracked repository
+    :param username: a string of the (FAS) user name
+    :param repo: object, current project git repository
+    :param repo_name: a string of the repository name
+    :param namespace: a string determines a type of the repository
+    :return: a bool; True if remote was created, False when already exists
+    """
+    parsed_url = urlparse(remote_base_url)
+    remote_url = '{0}://{1}/{2}/{3}.git'.format(
+        parsed_url.scheme,
+        parsed_url.netloc,
+        username,
+        repo_name,
+    )
+
+    # check already existing remote
+    for remote in repo.remotes:
+        if remote.name == username:
+            return False
+
+    try:
+        # create remote with username as its name
+        repo.create_remote(username, url=remote_url)
+    except git.exc.GitCommandError as e:
+        error_msg = "During create remote:\n  {0}\n  {1}".format(
+            " ".join(e.command), e.stderr)
+        raise rpkgError(error_msg)
+    return True
+
+
+def config_get_safely(config, section, option):
+    """
+    Returns option from the user's configuration file. In case of missing
+    section or option method throws an exception with a human-readable
+    warning and a possible hint.
+    The method should be used especially in situations when there are newly
+    added sections/options into the config. In this case, there is a risk that
+    the user's config wasn't properly upgraded.
+
+    :param config: ConfigParser object
+    :param section: section name in the config
+    :param option: name of the option
+    :return: option value from the right section
+    :rtype: str
+    """
+
+    hint = (
+        "First (if possible), refer to the help of the current command "
+        "(-h/--help).\n"
+        "There also might be a new version of the config after upgrade.\n"
+        "Hint: you can check if you have 'centpkg.conf.rpmnew' or "
+        "'centpkg.conf.rpmsave' in the config directory. If yes, try to merge "
+        "your changes to the config with the maintainer provided version "
+        "(or replace centpkg.conf file with 'centpkg.conf.rpmnew')."
+    )
+
+    try:
+        return config.get(section, option)
+    except NoSectionError:
+        msg = "Missing section '{0}' in the config file.".format(section)
+        raise rpkgError("{0}\n{1}".format(msg, hint))
+    except NoOptionError:
+        msg = "Missing option '{0}' in the section '{1}' of the config file.".format(
+            option, section
+        )
+        raise rpkgError("{0}\n{1}".format(msg, hint))
+    except Exception:
+        raise