We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Simplify vcs_support backend git by using GitPython
We already started using GitPython for some submodules pieces. We should continue porting some of our hand rolled logic to use GitPython instead.

Pieces of `readthedocs.vcs_support.backend.git` we should definitely port:

* `parse_branches()` -- this supersedes a branch I started at #2997 to replace parsing this with a csvreader
* `parse_tags()` -- same silly code using a csvreader
* `find_ref()`
* `ref_exists()`
* `repo_exists()`

Maybe:

* `tags()`
* `branches()`

These would be good first targets to port over, as we are executing these commands to get data out of the repository. It's not important that these messages are surfaced to users in build command output.

I'm going to block on getting a feature out that executes these commands in docker first though, as we need to clone and submodule checkout inside the docker container to isolate these calls. I can't quite consider how relying on gitpython for some of these calls, but not all, works with regard to docker vcs checkouts.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/vcs_support/backends/git.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 """Git-related utilities."""
3 
4 from __future__ import (
5     absolute_import,
6     division,
7     print_function,
8     unicode_literals,
9 )
10 
11 import logging
12 import os
13 import re
14 
15 import git
16 from builtins import str
17 from django.core.exceptions import ValidationError
18 from django.conf import settings
19 from git.exc import BadName
20 
21 from readthedocs.config import ALL
22 from readthedocs.projects.exceptions import RepositoryError
23 from readthedocs.projects.validators import validate_submodule_url
24 from readthedocs.vcs_support.base import BaseVCS, VCSVersion
25 
26 log = logging.getLogger(__name__)
27 
28 
29 class Backend(BaseVCS):
30 
31     """Git VCS backend."""
32 
33     supports_tags = True
34     supports_branches = True
35     supports_submodules = True
36     fallback_branch = 'master'  # default branch
37     repo_depth = 50
38 
39     def __init__(self, *args, **kwargs):
40         super(Backend, self).__init__(*args, **kwargs)
41         self.token = kwargs.get('token', None)
42         self.repo_url = self._get_clone_url()
43 
44     def _get_clone_url(self):
45         if '://' in self.repo_url:
46             hacked_url = self.repo_url.split('://')[1]
47             hacked_url = re.sub('.git$', '', hacked_url)
48             clone_url = 'https://%s' % hacked_url
49             if self.token:
50                 clone_url = 'https://%s@%s' % (self.token, hacked_url)
51                 return clone_url
52             # Don't edit URL because all hosts aren't the same
53             # else:
54             #     clone_url = 'git://%s' % (hacked_url)
55         return self.repo_url
56 
57     def set_remote_url(self, url):
58         return self.run('git', 'remote', 'set-url', 'origin', url)
59 
60     def update(self):
61         """Clone or update the repository."""
62         super(Backend, self).update()
63         if self.repo_exists():
64             self.set_remote_url(self.repo_url)
65             return self.fetch()
66         self.make_clean_working_dir()
67         return self.clone()
68 
69     def repo_exists(self):
70         code, _, _ = self.run('git', 'status', record=False)
71         return code == 0
72 
73     def are_submodules_available(self, config):
74         """Test whether git submodule checkout step should be performed."""
75         # TODO remove this after users migrate to a config file
76         from readthedocs.projects.models import Feature
77         submodules_in_config = (
78             config.submodules.exclude != ALL or
79             config.submodules.include
80         )
81         if (self.project.has_feature(Feature.SKIP_SUBMODULES) or
82                 not submodules_in_config):
83             return False
84 
85         # Keep compatibility with previous projects
86         code, out, _ = self.run('git', 'submodule', 'status', record=False)
87         return code == 0 and bool(out)
88 
89     def validate_submodules(self, config):
90         """
91         Returns the submodules and check that its URLs are valid.
92 
93         .. note::
94 
95            Allways call after `self.are_submodules_available`.
96 
97         :returns: tuple(bool, list)
98 
99         Returns true if all required submodules URLs are valid.
100         Returns a list of all required submodules:
101         - Include is `ALL`, returns all submodules avaliable.
102         - Include is a list, returns just those.
103         - Exclude is `ALL` - this should never happen.
104         - Exlude is a list, returns all avaliable submodules
105           but those from the list.
106         """
107         repo = git.Repo(self.working_dir)
108         submodules = {
109             sub.path: sub
110             for sub in repo.submodules
111         }
112 
113         for sub_path in config.submodules.exclude:
114             path = sub_path.rstrip('/')
115             if path in submodules:
116                 del submodules[path]
117 
118         if config.submodules.include != ALL and config.submodules.include:
119             submodules_include = {}
120             for sub_path in config.submodules.include:
121                 path = sub_path.rstrip('/')
122                 submodules_include[path] = submodules[path]
123             submodules = submodules_include
124 
125         for path, submodule in submodules.items():
126             try:
127                 validate_submodule_url(submodule.url)
128             except ValidationError:
129                 return False, []
130         return True, submodules.keys()
131 
132     def fetch(self):
133         code, stdout, stderr = self.run(
134             'git', 'fetch', '--depth', str(self.repo_depth),
135             '--tags', '--prune', '--prune-tags',
136         )
137         if code != 0:
138             raise RepositoryError
139         return code, stdout, stderr
140 
141     def checkout_revision(self, revision=None):
142         if not revision:
143             branch = self.default_branch or self.fallback_branch
144             revision = 'origin/%s' % branch
145 
146         code, out, err = self.run('git', 'checkout', '--force', revision)
147         if code != 0:
148             log.warning("Failed to checkout revision '%s': %s", revision, code)
149         return [code, out, err]
150 
151     def clone(self):
152         """Clones the repository."""
153         code, stdout, stderr = self.run(
154             'git', 'clone', '--depth', str(self.repo_depth),
155             '--no-single-branch', self.repo_url, '.'
156         )
157         if code != 0:
158             raise RepositoryError
159         return code, stdout, stderr
160 
161     @property
162     def tags(self):
163         versions = []
164         repo = git.Repo(self.working_dir)
165         for tag in repo.tags:
166             try:
167                 versions.append(VCSVersion(self, str(tag.commit), str(tag)))
168             except ValueError as e:
169                 # ValueError: Cannot resolve commit as tag TAGNAME points to a
170                 # blob object - use the `.object` property instead to access it
171                 # This is not a real tag for us, so we skip it
172                 # https://github.com/rtfd/readthedocs.org/issues/4440
173                 log.warning('Git tag skipped: %s', tag, exc_info=True)
174                 continue
175         return versions
176 
177     @property
178     def branches(self):
179         repo = git.Repo(self.working_dir)
180         versions = []
181         branches = []
182 
183         # ``repo.remotes.origin.refs`` returns remote branches
184         if repo.remotes:
185             branches += repo.remotes.origin.refs
186 
187         for branch in branches:
188             verbose_name = branch.name
189             if verbose_name.startswith('origin/'):
190                 verbose_name = verbose_name.replace('origin/', '')
191             if verbose_name == 'HEAD':
192                 continue
193             versions.append(VCSVersion(self, str(branch), verbose_name))
194         return versions
195 
196     @property
197     def commit(self):
198         _, stdout, _ = self.run('git', 'rev-parse', 'HEAD')
199         return stdout.strip()
200 
201     def checkout(self, identifier=None):
202         """Checkout to identifier or latest."""
203         super(Backend, self).checkout()
204         # Find proper identifier
205         if not identifier:
206             identifier = self.default_branch or self.fallback_branch
207 
208         identifier = self.find_ref(identifier)
209 
210         # Checkout the correct identifier for this branch.
211         code, out, err = self.checkout_revision(identifier)
212         if code != 0:
213             return code, out, err
214 
215         # Clean any remains of previous checkouts
216         self.run('git', 'clean', '-d', '-f', '-f')
217         return code, out, err
218 
219     def update_submodules(self, config):
220         if self.are_submodules_available(config):
221             valid, submodules = self.validate_submodules(config)
222             if valid:
223                 self.checkout_submodules(submodules, config)
224             else:
225                 raise RepositoryError(RepositoryError.INVALID_SUBMODULES)
226 
227     def checkout_submodules(self, submodules, config):
228         """Checkout all repository submodules."""
229         self.run('git', 'submodule', 'sync')
230         cmd = [
231             'git',
232             'submodule',
233             'update',
234             '--init',
235             '--force',
236         ]
237         if config.submodules.recursive:
238             cmd.append('--recursive')
239         cmd += submodules
240         self.run(*cmd)
241 
242     def find_ref(self, ref):
243         # Check if ref starts with 'origin/'
244         if ref.startswith('origin/'):
245             return ref
246 
247         # Check if ref is a branch of the origin remote
248         if self.ref_exists('remotes/origin/' + ref):
249             return 'origin/' + ref
250 
251         return ref
252 
253     def ref_exists(self, ref):
254         try:
255             r = git.Repo(self.working_dir)
256             if r.commit(ref):
257                 return True
258         except (BadName, ValueError):
259             return False
260         return False
261 
262     @property
263     def env(self):
264         env = super(Backend, self).env
265         env['GIT_DIR'] = os.path.join(self.working_dir, '.git')
266         # Don't prompt for username, this requires Git 2.3+
267         env['GIT_TERMINAL_PROMPT'] = '0'
268         return env
269
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py
--- a/readthedocs/vcs_support/backends/git.py
+++ b/readthedocs/vcs_support/backends/git.py
@@ -15,8 +15,7 @@
 import git
 from builtins import str
 from django.core.exceptions import ValidationError
-from django.conf import settings
-from git.exc import BadName
+from git.exc import BadName, InvalidGitRepositoryError
 
 from readthedocs.config import ALL
 from readthedocs.projects.exceptions import RepositoryError
@@ -67,8 +66,11 @@
         return self.clone()
 
     def repo_exists(self):
-        code, _, _ = self.run('git', 'status', record=False)
-        return code == 0
+        try:
+            git.Repo(self.working_dir)
+        except InvalidGitRepositoryError:
+            return False
+        return True
 
     def are_submodules_available(self, config):
         """Test whether git submodule checkout step should be performed."""
@@ -83,8 +85,8 @@
             return False
 
         # Keep compatibility with previous projects
-        code, out, _ = self.run('git', 'submodule', 'status', record=False)
-        return code == 0 and bool(out)
+        repo = git.Repo(self.working_dir)
+        return bool(repo.submodules)
 
     def validate_submodules(self, config):
         """

{"golden_diff": "diff --git a/readthedocs/vcs_support/backends/git.py b/readthedocs/vcs_support/backends/git.py\n--- a/readthedocs/vcs_support/backends/git.py\n+++ b/readthedocs/vcs_support/backends/git.py\n@@ -15,8 +15,7 @@\n import git\n from builtins import str\n from django.core.exceptions import ValidationError\n-from django.conf import settings\n-from git.exc import BadName\n+from git.exc import BadName, InvalidGitRepositoryError\n \n from readthedocs.config import ALL\n from readthedocs.projects.exceptions import RepositoryError\n@@ -67,8 +66,11 @@\n         return self.clone()\n \n     def repo_exists(self):\n-        code, _, _ = self.run('git', 'status', record=False)\n-        return code == 0\n+        try:\n+            git.Repo(self.working_dir)\n+        except InvalidGitRepositoryError:\n+            return False\n+        return True\n \n     def are_submodules_available(self, config):\n         \"\"\"Test whether git submodule checkout step should be performed.\"\"\"\n@@ -83,8 +85,8 @@\n             return False\n \n         # Keep compatibility with previous projects\n-        code, out, _ = self.run('git', 'submodule', 'status', record=False)\n-        return code == 0 and bool(out)\n+        repo = git.Repo(self.working_dir)\n+        return bool(repo.submodules)\n \n     def validate_submodules(self, config):\n         \"\"\"\n", "issue": "Simplify vcs_support backend git by using GitPython\nWe already started using GitPython for some submodules pieces. We should continue porting some of our hand rolled logic to use GitPython instead.\r\n\r\nPieces of `readthedocs.vcs_support.backend.git` we should definitely port:\r\n\r\n* `parse_branches()` -- this supersedes a branch I started at #2997 to replace parsing this with a csvreader\r\n* `parse_tags()` -- same silly code using a csvreader\r\n* `find_ref()`\r\n* `ref_exists()`\r\n* `repo_exists()`\r\n\r\nMaybe:\r\n\r\n* `tags()`\r\n* `branches()`\r\n\r\nThese would be good first targets to port over, as we are executing these commands to get data out of the repository. It's not important that these messages are surfaced to users in build command output.\r\n\r\nI'm going to block on getting a feature out that executes these commands in docker first though, as we need to clone and submodule checkout inside the docker container to isolate these calls. I can't quite consider how relying on gitpython for some of these calls, but not all, works with regard to docker vcs checkouts.\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Git-related utilities.\"\"\"\n\nfrom __future__ import (\n    absolute_import,\n    division,\n    print_function,\n    unicode_literals,\n)\n\nimport logging\nimport os\nimport re\n\nimport git\nfrom builtins import str\nfrom django.core.exceptions import ValidationError\nfrom django.conf import settings\nfrom git.exc import BadName\n\nfrom readthedocs.config import ALL\nfrom readthedocs.projects.exceptions import RepositoryError\nfrom readthedocs.projects.validators import validate_submodule_url\nfrom readthedocs.vcs_support.base import BaseVCS, VCSVersion\n\nlog = logging.getLogger(__name__)\n\n\nclass Backend(BaseVCS):\n\n    \"\"\"Git VCS backend.\"\"\"\n\n    supports_tags = True\n    supports_branches = True\n    supports_submodules = True\n    fallback_branch = 'master'  # default branch\n    repo_depth = 50\n\n    def __init__(self, *args, **kwargs):\n        super(Backend, self).__init__(*args, **kwargs)\n        self.token = kwargs.get('token', None)\n        self.repo_url = self._get_clone_url()\n\n    def _get_clone_url(self):\n        if '://' in self.repo_url:\n            hacked_url = self.repo_url.split('://')[1]\n            hacked_url = re.sub('.git$', '', hacked_url)\n            clone_url = 'https://%s' % hacked_url\n            if self.token:\n                clone_url = 'https://%s@%s' % (self.token, hacked_url)\n                return clone_url\n            # Don't edit URL because all hosts aren't the same\n            # else:\n            #     clone_url = 'git://%s' % (hacked_url)\n        return self.repo_url\n\n    def set_remote_url(self, url):\n        return self.run('git', 'remote', 'set-url', 'origin', url)\n\n    def update(self):\n        \"\"\"Clone or update the repository.\"\"\"\n        super(Backend, self).update()\n        if self.repo_exists():\n            self.set_remote_url(self.repo_url)\n            return self.fetch()\n        self.make_clean_working_dir()\n        return self.clone()\n\n    def repo_exists(self):\n        code, _, _ = self.run('git', 'status', record=False)\n        return code == 0\n\n    def are_submodules_available(self, config):\n        \"\"\"Test whether git submodule checkout step should be performed.\"\"\"\n        # TODO remove this after users migrate to a config file\n        from readthedocs.projects.models import Feature\n        submodules_in_config = (\n            config.submodules.exclude != ALL or\n            config.submodules.include\n        )\n        if (self.project.has_feature(Feature.SKIP_SUBMODULES) or\n                not submodules_in_config):\n            return False\n\n        # Keep compatibility with previous projects\n        code, out, _ = self.run('git', 'submodule', 'status', record=False)\n        return code == 0 and bool(out)\n\n    def validate_submodules(self, config):\n        \"\"\"\n        Returns the submodules and check that its URLs are valid.\n\n        .. note::\n\n           Allways call after `self.are_submodules_available`.\n\n        :returns: tuple(bool, list)\n\n        Returns true if all required submodules URLs are valid.\n        Returns a list of all required submodules:\n        - Include is `ALL`, returns all submodules avaliable.\n        - Include is a list, returns just those.\n        - Exclude is `ALL` - this should never happen.\n        - Exlude is a list, returns all avaliable submodules\n          but those from the list.\n        \"\"\"\n        repo = git.Repo(self.working_dir)\n        submodules = {\n            sub.path: sub\n            for sub in repo.submodules\n        }\n\n        for sub_path in config.submodules.exclude:\n            path = sub_path.rstrip('/')\n            if path in submodules:\n                del submodules[path]\n\n        if config.submodules.include != ALL and config.submodules.include:\n            submodules_include = {}\n            for sub_path in config.submodules.include:\n                path = sub_path.rstrip('/')\n                submodules_include[path] = submodules[path]\n            submodules = submodules_include\n\n        for path, submodule in submodules.items():\n            try:\n                validate_submodule_url(submodule.url)\n            except ValidationError:\n                return False, []\n        return True, submodules.keys()\n\n    def fetch(self):\n        code, stdout, stderr = self.run(\n            'git', 'fetch', '--depth', str(self.repo_depth),\n            '--tags', '--prune', '--prune-tags',\n        )\n        if code != 0:\n            raise RepositoryError\n        return code, stdout, stderr\n\n    def checkout_revision(self, revision=None):\n        if not revision:\n            branch = self.default_branch or self.fallback_branch\n            revision = 'origin/%s' % branch\n\n        code, out, err = self.run('git', 'checkout', '--force', revision)\n        if code != 0:\n            log.warning(\"Failed to checkout revision '%s': %s\", revision, code)\n        return [code, out, err]\n\n    def clone(self):\n        \"\"\"Clones the repository.\"\"\"\n        code, stdout, stderr = self.run(\n            'git', 'clone', '--depth', str(self.repo_depth),\n            '--no-single-branch', self.repo_url, '.'\n        )\n        if code != 0:\n            raise RepositoryError\n        return code, stdout, stderr\n\n    @property\n    def tags(self):\n        versions = []\n        repo = git.Repo(self.working_dir)\n        for tag in repo.tags:\n            try:\n                versions.append(VCSVersion(self, str(tag.commit), str(tag)))\n            except ValueError as e:\n                # ValueError: Cannot resolve commit as tag TAGNAME points to a\n                # blob object - use the `.object` property instead to access it\n                # This is not a real tag for us, so we skip it\n                # https://github.com/rtfd/readthedocs.org/issues/4440\n                log.warning('Git tag skipped: %s', tag, exc_info=True)\n                continue\n        return versions\n\n    @property\n    def branches(self):\n        repo = git.Repo(self.working_dir)\n        versions = []\n        branches = []\n\n        # ``repo.remotes.origin.refs`` returns remote branches\n        if repo.remotes:\n            branches += repo.remotes.origin.refs\n\n        for branch in branches:\n            verbose_name = branch.name\n            if verbose_name.startswith('origin/'):\n                verbose_name = verbose_name.replace('origin/', '')\n            if verbose_name == 'HEAD':\n                continue\n            versions.append(VCSVersion(self, str(branch), verbose_name))\n        return versions\n\n    @property\n    def commit(self):\n        _, stdout, _ = self.run('git', 'rev-parse', 'HEAD')\n        return stdout.strip()\n\n    def checkout(self, identifier=None):\n        \"\"\"Checkout to identifier or latest.\"\"\"\n        super(Backend, self).checkout()\n        # Find proper identifier\n        if not identifier:\n            identifier = self.default_branch or self.fallback_branch\n\n        identifier = self.find_ref(identifier)\n\n        # Checkout the correct identifier for this branch.\n        code, out, err = self.checkout_revision(identifier)\n        if code != 0:\n            return code, out, err\n\n        # Clean any remains of previous checkouts\n        self.run('git', 'clean', '-d', '-f', '-f')\n        return code, out, err\n\n    def update_submodules(self, config):\n        if self.are_submodules_available(config):\n            valid, submodules = self.validate_submodules(config)\n            if valid:\n                self.checkout_submodules(submodules, config)\n            else:\n                raise RepositoryError(RepositoryError.INVALID_SUBMODULES)\n\n    def checkout_submodules(self, submodules, config):\n        \"\"\"Checkout all repository submodules.\"\"\"\n        self.run('git', 'submodule', 'sync')\n        cmd = [\n            'git',\n            'submodule',\n            'update',\n            '--init',\n            '--force',\n        ]\n        if config.submodules.recursive:\n            cmd.append('--recursive')\n        cmd += submodules\n        self.run(*cmd)\n\n    def find_ref(self, ref):\n        # Check if ref starts with 'origin/'\n        if ref.startswith('origin/'):\n            return ref\n\n        # Check if ref is a branch of the origin remote\n        if self.ref_exists('remotes/origin/' + ref):\n            return 'origin/' + ref\n\n        return ref\n\n    def ref_exists(self, ref):\n        try:\n            r = git.Repo(self.working_dir)\n            if r.commit(ref):\n                return True\n        except (BadName, ValueError):\n            return False\n        return False\n\n    @property\n    def env(self):\n        env = super(Backend, self).env\n        env['GIT_DIR'] = os.path.join(self.working_dir, '.git')\n        # Don't prompt for username, this requires Git 2.3+\n        env['GIT_TERMINAL_PROMPT'] = '0'\n        return env\n", "path": "readthedocs/vcs_support/backends/git.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Git-related utilities.\"\"\"\n\nfrom __future__ import (\n    absolute_import,\n    division,\n    print_function,\n    unicode_literals,\n)\n\nimport logging\nimport os\nimport re\n\nimport git\nfrom builtins import str\nfrom django.core.exceptions import ValidationError\nfrom git.exc import BadName, InvalidGitRepositoryError\n\nfrom readthedocs.config import ALL\nfrom readthedocs.projects.exceptions import RepositoryError\nfrom readthedocs.projects.validators import validate_submodule_url\nfrom readthedocs.vcs_support.base import BaseVCS, VCSVersion\n\nlog = logging.getLogger(__name__)\n\n\nclass Backend(BaseVCS):\n\n    \"\"\"Git VCS backend.\"\"\"\n\n    supports_tags = True\n    supports_branches = True\n    supports_submodules = True\n    fallback_branch = 'master'  # default branch\n    repo_depth = 50\n\n    def __init__(self, *args, **kwargs):\n        super(Backend, self).__init__(*args, **kwargs)\n        self.token = kwargs.get('token', None)\n        self.repo_url = self._get_clone_url()\n\n    def _get_clone_url(self):\n        if '://' in self.repo_url:\n            hacked_url = self.repo_url.split('://')[1]\n            hacked_url = re.sub('.git$', '', hacked_url)\n            clone_url = 'https://%s' % hacked_url\n            if self.token:\n                clone_url = 'https://%s@%s' % (self.token, hacked_url)\n                return clone_url\n            # Don't edit URL because all hosts aren't the same\n            # else:\n            #     clone_url = 'git://%s' % (hacked_url)\n        return self.repo_url\n\n    def set_remote_url(self, url):\n        return self.run('git', 'remote', 'set-url', 'origin', url)\n\n    def update(self):\n        \"\"\"Clone or update the repository.\"\"\"\n        super(Backend, self).update()\n        if self.repo_exists():\n            self.set_remote_url(self.repo_url)\n            return self.fetch()\n        self.make_clean_working_dir()\n        return self.clone()\n\n    def repo_exists(self):\n        try:\n            git.Repo(self.working_dir)\n        except InvalidGitRepositoryError:\n            return False\n        return True\n\n    def are_submodules_available(self, config):\n        \"\"\"Test whether git submodule checkout step should be performed.\"\"\"\n        # TODO remove this after users migrate to a config file\n        from readthedocs.projects.models import Feature\n        submodules_in_config = (\n            config.submodules.exclude != ALL or\n            config.submodules.include\n        )\n        if (self.project.has_feature(Feature.SKIP_SUBMODULES) or\n                not submodules_in_config):\n            return False\n\n        # Keep compatibility with previous projects\n        repo = git.Repo(self.working_dir)\n        return bool(repo.submodules)\n\n    def validate_submodules(self, config):\n        \"\"\"\n        Returns the submodules and check that its URLs are valid.\n\n        .. note::\n\n           Allways call after `self.are_submodules_available`.\n\n        :returns: tuple(bool, list)\n\n        Returns true if all required submodules URLs are valid.\n        Returns a list of all required submodules:\n        - Include is `ALL`, returns all submodules avaliable.\n        - Include is a list, returns just those.\n        - Exclude is `ALL` - this should never happen.\n        - Exlude is a list, returns all avaliable submodules\n          but those from the list.\n        \"\"\"\n        repo = git.Repo(self.working_dir)\n        submodules = {\n            sub.path: sub\n            for sub in repo.submodules\n        }\n\n        for sub_path in config.submodules.exclude:\n            path = sub_path.rstrip('/')\n            if path in submodules:\n                del submodules[path]\n\n        if config.submodules.include != ALL and config.submodules.include:\n            submodules_include = {}\n            for sub_path in config.submodules.include:\n                path = sub_path.rstrip('/')\n                submodules_include[path] = submodules[path]\n            submodules = submodules_include\n\n        for path, submodule in submodules.items():\n            try:\n                validate_submodule_url(submodule.url)\n            except ValidationError:\n                return False, []\n        return True, submodules.keys()\n\n    def fetch(self):\n        code, stdout, stderr = self.run(\n            'git', 'fetch', '--depth', str(self.repo_depth),\n            '--tags', '--prune', '--prune-tags',\n        )\n        if code != 0:\n            raise RepositoryError\n        return code, stdout, stderr\n\n    def checkout_revision(self, revision=None):\n        if not revision:\n            branch = self.default_branch or self.fallback_branch\n            revision = 'origin/%s' % branch\n\n        code, out, err = self.run('git', 'checkout', '--force', revision)\n        if code != 0:\n            log.warning(\"Failed to checkout revision '%s': %s\", revision, code)\n        return [code, out, err]\n\n    def clone(self):\n        \"\"\"Clones the repository.\"\"\"\n        code, stdout, stderr = self.run(\n            'git', 'clone', '--depth', str(self.repo_depth),\n            '--no-single-branch', self.repo_url, '.'\n        )\n        if code != 0:\n            raise RepositoryError\n        return code, stdout, stderr\n\n    @property\n    def tags(self):\n        versions = []\n        repo = git.Repo(self.working_dir)\n        for tag in repo.tags:\n            try:\n                versions.append(VCSVersion(self, str(tag.commit), str(tag)))\n            except ValueError as e:\n                # ValueError: Cannot resolve commit as tag TAGNAME points to a\n                # blob object - use the `.object` property instead to access it\n                # This is not a real tag for us, so we skip it\n                # https://github.com/rtfd/readthedocs.org/issues/4440\n                log.warning('Git tag skipped: %s', tag, exc_info=True)\n                continue\n        return versions\n\n    @property\n    def branches(self):\n        repo = git.Repo(self.working_dir)\n        versions = []\n        branches = []\n\n        # ``repo.remotes.origin.refs`` returns remote branches\n        if repo.remotes:\n            branches += repo.remotes.origin.refs\n\n        for branch in branches:\n            verbose_name = branch.name\n            if verbose_name.startswith('origin/'):\n                verbose_name = verbose_name.replace('origin/', '')\n            if verbose_name == 'HEAD':\n                continue\n            versions.append(VCSVersion(self, str(branch), verbose_name))\n        return versions\n\n    @property\n    def commit(self):\n        _, stdout, _ = self.run('git', 'rev-parse', 'HEAD')\n        return stdout.strip()\n\n    def checkout(self, identifier=None):\n        \"\"\"Checkout to identifier or latest.\"\"\"\n        super(Backend, self).checkout()\n        # Find proper identifier\n        if not identifier:\n            identifier = self.default_branch or self.fallback_branch\n\n        identifier = self.find_ref(identifier)\n\n        # Checkout the correct identifier for this branch.\n        code, out, err = self.checkout_revision(identifier)\n        if code != 0:\n            return code, out, err\n\n        # Clean any remains of previous checkouts\n        self.run('git', 'clean', '-d', '-f', '-f')\n        return code, out, err\n\n    def update_submodules(self, config):\n        if self.are_submodules_available(config):\n            valid, submodules = self.validate_submodules(config)\n            if valid:\n                self.checkout_submodules(submodules, config)\n            else:\n                raise RepositoryError(RepositoryError.INVALID_SUBMODULES)\n\n    def checkout_submodules(self, submodules, config):\n        \"\"\"Checkout all repository submodules.\"\"\"\n        self.run('git', 'submodule', 'sync')\n        cmd = [\n            'git',\n            'submodule',\n            'update',\n            '--init',\n            '--force',\n        ]\n        if config.submodules.recursive:\n            cmd.append('--recursive')\n        cmd += submodules\n        self.run(*cmd)\n\n    def find_ref(self, ref):\n        # Check if ref starts with 'origin/'\n        if ref.startswith('origin/'):\n            return ref\n\n        # Check if ref is a branch of the origin remote\n        if self.ref_exists('remotes/origin/' + ref):\n            return 'origin/' + ref\n\n        return ref\n\n    def ref_exists(self, ref):\n        try:\n            r = git.Repo(self.working_dir)\n            if r.commit(ref):\n                return True\n        except (BadName, ValueError):\n            return False\n        return False\n\n    @property\n    def env(self):\n        env = super(Backend, self).env\n        env['GIT_DIR'] = os.path.join(self.working_dir, '.git')\n        # Don't prompt for username, this requires Git 2.3+\n        env['GIT_TERMINAL_PROMPT'] = '0'\n        return env\n", "path": "readthedocs/vcs_support/backends/git.py"}]}