We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Stable builds are triggered recursively
On Sunday we found that a build for the `stable` version was triggered constantly (Example, http://readthedocs.org/projects/bugzilla/builds/)

@ericholscher hotfixed this with this commit https://github.com/rtfd/readthedocs.org/commit/83caf8fe8 to avoid the "Sunday problem", but we need to research and fix it properly.

We suspect that this could be introduced on #4433 and/or #4876.



--- 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 git.exc import BadName
19 
20 from readthedocs.config import ALL
21 from readthedocs.projects.exceptions import RepositoryError
22 from readthedocs.projects.validators import validate_submodule_url
23 from readthedocs.vcs_support.base import BaseVCS, VCSVersion
24 
25 log = logging.getLogger(__name__)
26 
27 
28 class Backend(BaseVCS):
29 
30     """Git VCS backend."""
31 
32     supports_tags = True
33     supports_branches = True
34     supports_submodules = True
35     fallback_branch = 'master'  # default branch
36     repo_depth = 50
37 
38     def __init__(self, *args, **kwargs):
39         super(Backend, self).__init__(*args, **kwargs)
40         self.token = kwargs.get('token', None)
41         self.repo_url = self._get_clone_url()
42 
43     def _get_clone_url(self):
44         if '://' in self.repo_url:
45             hacked_url = self.repo_url.split('://')[1]
46             hacked_url = re.sub('.git$', '', hacked_url)
47             clone_url = 'https://%s' % hacked_url
48             if self.token:
49                 clone_url = 'https://%s@%s' % (self.token, hacked_url)
50                 return clone_url
51             # Don't edit URL because all hosts aren't the same
52             # else:
53             #     clone_url = 'git://%s' % (hacked_url)
54         return self.repo_url
55 
56     def set_remote_url(self, url):
57         return self.run('git', 'remote', 'set-url', 'origin', url)
58 
59     def update(self):
60         """Clone or update the repository."""
61         super(Backend, self).update()
62         if self.repo_exists():
63             self.set_remote_url(self.repo_url)
64             return self.fetch()
65         self.make_clean_working_dir()
66         return self.clone()
67 
68     def repo_exists(self):
69         code, _, _ = self.run('git', 'status', record=False)
70         return code == 0
71 
72     def are_submodules_available(self, config):
73         """Test whether git submodule checkout step should be performed."""
74         # TODO remove this after users migrate to a config file
75         from readthedocs.projects.models import Feature
76         submodules_in_config = (
77             config.submodules.exclude != ALL or
78             config.submodules.include
79         )
80         if (self.project.has_feature(Feature.SKIP_SUBMODULES) or
81                 not submodules_in_config):
82             return False
83 
84         # Keep compatibility with previous projects
85         code, out, _ = self.run('git', 'submodule', 'status', record=False)
86         return code == 0 and bool(out)
87 
88     def validate_submodules(self, config):
89         """
90         Returns the submodules and check that its URLs are valid.
91 
92         .. note::
93 
94            Allways call after `self.are_submodules_available`.
95 
96         :returns: tuple(bool, list)
97 
98         Returns true if all required submodules URLs are valid.
99         Returns a list of all required submodules:
100         - Include is `ALL`, returns all submodules avaliable.
101         - Include is a list, returns just those.
102         - Exclude is `ALL` - this should never happen.
103         - Exlude is a list, returns all avaliable submodules
104           but those from the list.
105         """
106         repo = git.Repo(self.working_dir)
107         submodules = {
108             sub.path: sub
109             for sub in repo.submodules
110         }
111 
112         for sub_path in config.submodules.exclude:
113             path = sub_path.rstrip('/')
114             if path in submodules:
115                 del submodules[path]
116 
117         if config.submodules.include != ALL and config.submodules.include:
118             submodules_include = {}
119             for sub_path in config.submodules.include:
120                 path = sub_path.rstrip('/')
121                 submodules_include[path] = submodules[path]
122             submodules = submodules_include
123 
124         for path, submodule in submodules.items():
125             try:
126                 validate_submodule_url(submodule.url)
127             except ValidationError:
128                 return False, []
129         return True, submodules.keys()
130 
131     def fetch(self):
132         code, stdout, stderr = self.run(
133             'git', 'fetch', '--depth', str(self.repo_depth),
134             '--tags', '--prune', '--prune-tags',
135         )
136         if code != 0:
137             raise RepositoryError
138         return code, stdout, stderr
139 
140     def checkout_revision(self, revision=None):
141         if not revision:
142             branch = self.default_branch or self.fallback_branch
143             revision = 'origin/%s' % branch
144 
145         code, out, err = self.run('git', 'checkout', '--force', revision)
146         if code != 0:
147             log.warning("Failed to checkout revision '%s': %s", revision, code)
148         return [code, out, err]
149 
150     def clone(self):
151         """Clones the repository."""
152         code, stdout, stderr = self.run(
153             'git', 'clone', '--depth', str(self.repo_depth),
154             '--no-single-branch', self.repo_url, '.'
155         )
156         if code != 0:
157             raise RepositoryError
158         return code, stdout, stderr
159 
160     @property
161     def tags(self):
162         versions = []
163         repo = git.Repo(self.working_dir)
164         for tag in repo.tags:
165             try:
166                 versions.append(VCSVersion(self, str(tag.commit), str(tag)))
167             except ValueError as e:
168                 # ValueError: Cannot resolve commit as tag TAGNAME points to a
169                 # blob object - use the `.object` property instead to access it
170                 # This is not a real tag for us, so we skip it
171                 # https://github.com/rtfd/readthedocs.org/issues/4440
172                 log.warning('Git tag skipped: %s', tag, exc_info=True)
173                 continue
174         return versions
175 
176     @property
177     def branches(self):
178         repo = git.Repo(self.working_dir)
179         versions = []
180 
181         # ``repo.branches`` returns local branches and
182         branches = repo.branches
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,6 +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 readthedocs.config import ALL
@@ -177,12 +178,13 @@
     def branches(self):
         repo = git.Repo(self.working_dir)
         versions = []
+        branches = []
 
-        # ``repo.branches`` returns local branches and
-        branches = repo.branches
         # ``repo.remotes.origin.refs`` returns remote branches
         if repo.remotes:
             branches += repo.remotes.origin.refs
+        if getattr(settings, 'LOCAL_GIT_BRANCHES', False):
+            branches += repo.branches
 
         for branch in branches:
             verbose_name = branch.name

{"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,6 +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 \n from readthedocs.config import ALL\n@@ -177,12 +178,13 @@\n     def branches(self):\n         repo = git.Repo(self.working_dir)\n         versions = []\n+        branches = []\n \n-        # ``repo.branches`` returns local branches and\n-        branches = repo.branches\n         # ``repo.remotes.origin.refs`` returns remote branches\n         if repo.remotes:\n             branches += repo.remotes.origin.refs\n+        if getattr(settings, 'LOCAL_GIT_BRANCHES', False):\n+            branches += repo.branches\n \n         for branch in branches:\n             verbose_name = branch.name\n", "issue": "Stable builds are triggered recursively\nOn Sunday we found that a build for the `stable` version was triggered constantly (Example, http://readthedocs.org/projects/bugzilla/builds/)\r\n\r\n@ericholscher hotfixed this with this commit https://github.com/rtfd/readthedocs.org/commit/83caf8fe8 to avoid the \"Sunday problem\", but we need to research and fix it properly.\r\n\r\nWe suspect that this could be introduced on #4433 and/or #4876.\r\n\r\n\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 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\n        # ``repo.branches`` returns local branches and\n        branches = repo.branches\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 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        if getattr(settings, 'LOCAL_GIT_BRANCHES', False):\n            branches += repo.branches\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"}]}