We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Stable versions are broken
For instance: http://readthedocs.org/projects/docs/builds/6938938/

--- 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, division, print_function, unicode_literals)
6 
7 import csv
8 import logging
9 import os
10 import re
11 
12 from django.core.exceptions import ValidationError
13 import git
14 from six import PY2, StringIO
15 
16 from readthedocs.core.validators import validate_repository_url
17 from readthedocs.projects.exceptions import RepositoryError
18 from readthedocs.vcs_support.base import BaseVCS, VCSVersion
19 
20 log = logging.getLogger(__name__)
21 
22 
23 class Backend(BaseVCS):
24 
25     """Git VCS backend."""
26 
27     supports_tags = True
28     supports_branches = True
29     fallback_branch = 'master'  # default branch
30 
31     def __init__(self, *args, **kwargs):
32         super(Backend, self).__init__(*args, **kwargs)
33         self.token = kwargs.get('token', None)
34         self.repo_url = self._get_clone_url()
35 
36     def _get_clone_url(self):
37         if '://' in self.repo_url:
38             hacked_url = self.repo_url.split('://')[1]
39             hacked_url = re.sub('.git$', '', hacked_url)
40             clone_url = 'https://%s' % hacked_url
41             if self.token:
42                 clone_url = 'https://%s@%s' % (self.token, hacked_url)
43                 return clone_url
44             # Don't edit URL because all hosts aren't the same
45             # else:
46             #     clone_url = 'git://%s' % (hacked_url)
47         return self.repo_url
48 
49     def set_remote_url(self, url):
50         return self.run('git', 'remote', 'set-url', 'origin', url)
51 
52     def update(self):
53         # Use checkout() to update repo
54         self.checkout()
55 
56     def repo_exists(self):
57         code, _, _ = self.run('git', 'status', record=False)
58         return code == 0
59 
60     def are_submodules_available(self):
61         """
62         Test whether git submodule checkout step should be performed.
63 
64         .. note::
65 
66             Temporarily, we support skipping these steps as submodule step can
67             fail if using private submodules. This will eventually be
68             configureable with our YAML config.
69         """
70         # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30
71         from readthedocs.projects.models import Feature
72         if self.project.has_feature(Feature.SKIP_SUBMODULES):
73             return False
74         code, out, _ = self.run('git', 'submodule', 'status', record=False)
75         return code == 0 and bool(out)
76 
77     def are_submodules_valid(self):
78         """Test that all submodule URLs are valid."""
79         repo = git.Repo(self.working_dir)
80         for submodule in repo.submodules:
81             try:
82                 validate_repository_url(submodule.url)
83             except ValidationError:
84                 return False
85         return True
86 
87     def fetch(self):
88         code, _, _ = self.run('git', 'fetch', '--tags', '--prune')
89         if code != 0:
90             raise RepositoryError
91 
92     def checkout_revision(self, revision=None):
93         if not revision:
94             branch = self.default_branch or self.fallback_branch
95             revision = 'origin/%s' % branch
96 
97         code, out, err = self.run('git', 'checkout', '--force', revision)
98         if code != 0:
99             log.warning("Failed to checkout revision '%s': %s", revision, code)
100         return [code, out, err]
101 
102     def clone(self):
103         """
104         Clone the repository.
105 
106         .. note::
107 
108             Temporarily, we support skipping submodule recursive clone via a
109             feature flag. This will eventually be configureable with our YAML
110             config.
111         """
112         # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30
113         from readthedocs.projects.models import Feature
114         cmd = ['git', 'clone']
115         if not self.project.has_feature(Feature.SKIP_SUBMODULES):
116             cmd.append('--recursive')
117         cmd.extend([self.repo_url, '.'])
118         code, _, _ = self.run(*cmd)
119         if code != 0:
120             raise RepositoryError
121 
122     @property
123     def tags(self):
124         retcode, stdout, _ = self.run(
125             'git',
126             'show-ref',
127             '--tags',
128             record_as_success=True,
129         )
130         # error (or no tags found)
131         if retcode != 0:
132             return []
133         return self.parse_tags(stdout)
134 
135     def parse_tags(self, data):
136         """
137         Parses output of show-ref --tags, eg:
138 
139             3b32886c8d3cb815df3793b3937b2e91d0fb00f1 refs/tags/2.0.0
140             bd533a768ff661991a689d3758fcfe72f455435d refs/tags/2.0.1
141             c0288a17899b2c6818f74e3a90b77e2a1779f96a refs/tags/2.0.2
142             a63a2de628a3ce89034b7d1a5ca5e8159534eef0 refs/tags/2.1.0.beta2
143             c7fc3d16ed9dc0b19f0d27583ca661a64562d21e refs/tags/2.1.0.rc1
144             edc0a2d02a0cc8eae8b67a3a275f65cd126c05b1 refs/tags/2.1.0.rc2
145 
146         Into VCSTag objects with the tag name as verbose_name and the commit
147         hash as identifier.
148         """
149         # parse the lines into a list of tuples (commit-hash, tag ref name)
150         # StringIO below is expecting Unicode data, so ensure that it gets it.
151         if not isinstance(data, str):
152             data = str(data)
153         delimiter = str(' ').encode('utf-8') if PY2 else str(' ')
154         raw_tags = csv.reader(StringIO(data), delimiter=delimiter)
155         vcs_tags = []
156         for row in raw_tags:
157             row = [f for f in row if f != '']
158             if row == []:
159                 continue
160             commit_hash, name = row
161             clean_name = name.replace('refs/tags/', '')
162             vcs_tags.append(VCSVersion(self, commit_hash, clean_name))
163         return vcs_tags
164 
165     @property
166     def branches(self):
167         # Only show remote branches
168         retcode, stdout, _ = self.run(
169             'git',
170             'branch',
171             '-r',
172             record_as_success=True,
173         )
174         # error (or no branches found)
175         if retcode != 0:
176             return []
177         return self.parse_branches(stdout)
178 
179     def parse_branches(self, data):
180         """
181         Parse output of git branch -r.
182 
183         e.g.:
184 
185               origin/2.0.X
186               origin/HEAD -> origin/master
187               origin/develop
188               origin/master
189               origin/release/2.0.0
190               origin/release/2.1.0
191         """
192         clean_branches = []
193         # StringIO below is expecting Unicode data, so ensure that it gets it.
194         if not isinstance(data, str):
195             data = str(data)
196         delimiter = str(' ').encode('utf-8') if PY2 else str(' ')
197         raw_branches = csv.reader(StringIO(data), delimiter=delimiter)
198         for branch in raw_branches:
199             branch = [f for f in branch if f != '' and f != '*']
200             # Handle empty branches
201             if branch:
202                 branch = branch[0]
203                 if branch.startswith('origin/'):
204                     verbose_name = branch.replace('origin/', '')
205                     if verbose_name in ['HEAD']:
206                         continue
207                     clean_branches.append(
208                         VCSVersion(self, branch, verbose_name))
209                 else:
210                     clean_branches.append(VCSVersion(self, branch, branch))
211         return clean_branches
212 
213     @property
214     def commit(self):
215         _, stdout, _ = self.run('git', 'rev-parse', 'HEAD')
216         return stdout.strip()
217 
218     def checkout(self, identifier=None):
219         self.check_working_dir()
220 
221         # Clone or update repository
222         if self.repo_exists():
223             self.set_remote_url(self.repo_url)
224             self.fetch()
225         else:
226             self.make_clean_working_dir()
227             self.clone()
228 
229         # Find proper identifier
230         if not identifier:
231             identifier = self.default_branch or self.fallback_branch
232 
233         identifier = self.find_ref(identifier)
234 
235         # Checkout the correct identifier for this branch.
236         code, out, err = self.checkout_revision(identifier)
237         if code != 0:
238             return code, out, err
239 
240         # Clean any remains of previous checkouts
241         self.run('git', 'clean', '-d', '-f', '-f')
242 
243         # Update submodules, temporarily allow for skipping submodule checkout
244         # step for projects need more submodule configuration.
245         if self.are_submodules_available():
246             if self.are_submodules_valid():
247                 self.checkout_submodules()
248             else:
249                 raise RepositoryError(RepositoryError.INVALID_SUBMODULES)
250         return code, out, err
251 
252     def checkout_submodules(self):
253         """Checkout all repository submodules recursively."""
254         self.run('git', 'submodule', 'sync')
255         self.run(
256             'git',
257             'submodule',
258             'update',
259             '--init',
260             '--recursive',
261             '--force',
262         )
263 
264     def find_ref(self, ref):
265         # Check if ref starts with 'origin/'
266         if ref.startswith('origin/'):
267             return ref
268 
269         # Check if ref is a branch of the origin remote
270         if self.ref_exists('remotes/origin/' + ref):
271             return 'origin/' + ref
272 
273         return ref
274 
275     def ref_exists(self, ref):
276         code, _, _ = self.run('git', 'show-ref', ref, record_as_success=True)
277         return code == 0
278 
279     @property
280     def env(self):
281         env = super(Backend, self).env
282         env['GIT_DIR'] = os.path.join(self.working_dir, '.git')
283         # Don't prompt for username, this requires Git 2.3+
284         env['GIT_TERMINAL_PROMPT'] = '0'
285         return env
286
```
--- 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
@@ -9,8 +9,9 @@
 import os
 import re
 
-from django.core.exceptions import ValidationError
 import git
+from django.core.exceptions import ValidationError
+from git.exc import BadName
 from six import PY2, StringIO
 
 from readthedocs.core.validators import validate_repository_url
@@ -273,8 +274,13 @@
         return ref
 
     def ref_exists(self, ref):
-        code, _, _ = self.run('git', 'show-ref', ref, record_as_success=True)
-        return code == 0
+        try:
+            r = git.Repo(self.working_dir)
+            if r.commit(ref):
+                return True
+        except (BadName, ValueError):
+            return False
+        return False
 
     @property
     def env(self):

{"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@@ -9,8 +9,9 @@\n import os\n import re\n \n-from django.core.exceptions import ValidationError\n import git\n+from django.core.exceptions import ValidationError\n+from git.exc import BadName\n from six import PY2, StringIO\n \n from readthedocs.core.validators import validate_repository_url\n@@ -273,8 +274,13 @@\n         return ref\n \n     def ref_exists(self, ref):\n-        code, _, _ = self.run('git', 'show-ref', ref, record_as_success=True)\n-        return code == 0\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", "issue": "Stable versions are broken\nFor instance: http://readthedocs.org/projects/docs/builds/6938938/\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\"\"\"Git-related utilities.\"\"\"\n\nfrom __future__ import (\n    absolute_import, division, print_function, unicode_literals)\n\nimport csv\nimport logging\nimport os\nimport re\n\nfrom django.core.exceptions import ValidationError\nimport git\nfrom six import PY2, StringIO\n\nfrom readthedocs.core.validators import validate_repository_url\nfrom readthedocs.projects.exceptions import RepositoryError\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    fallback_branch = 'master'  # default branch\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        # Use checkout() to update repo\n        self.checkout()\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):\n        \"\"\"\n        Test whether git submodule checkout step should be performed.\n\n        .. note::\n\n            Temporarily, we support skipping these steps as submodule step can\n            fail if using private submodules. This will eventually be\n            configureable with our YAML config.\n        \"\"\"\n        # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30\n        from readthedocs.projects.models import Feature\n        if self.project.has_feature(Feature.SKIP_SUBMODULES):\n            return False\n        code, out, _ = self.run('git', 'submodule', 'status', record=False)\n        return code == 0 and bool(out)\n\n    def are_submodules_valid(self):\n        \"\"\"Test that all submodule URLs are valid.\"\"\"\n        repo = git.Repo(self.working_dir)\n        for submodule in repo.submodules:\n            try:\n                validate_repository_url(submodule.url)\n            except ValidationError:\n                return False\n        return True\n\n    def fetch(self):\n        code, _, _ = self.run('git', 'fetch', '--tags', '--prune')\n        if code != 0:\n            raise RepositoryError\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        \"\"\"\n        Clone the repository.\n\n        .. note::\n\n            Temporarily, we support skipping submodule recursive clone via a\n            feature flag. This will eventually be configureable with our YAML\n            config.\n        \"\"\"\n        # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30\n        from readthedocs.projects.models import Feature\n        cmd = ['git', 'clone']\n        if not self.project.has_feature(Feature.SKIP_SUBMODULES):\n            cmd.append('--recursive')\n        cmd.extend([self.repo_url, '.'])\n        code, _, _ = self.run(*cmd)\n        if code != 0:\n            raise RepositoryError\n\n    @property\n    def tags(self):\n        retcode, stdout, _ = self.run(\n            'git',\n            'show-ref',\n            '--tags',\n            record_as_success=True,\n        )\n        # error (or no tags found)\n        if retcode != 0:\n            return []\n        return self.parse_tags(stdout)\n\n    def parse_tags(self, data):\n        \"\"\"\n        Parses output of show-ref --tags, eg:\n\n            3b32886c8d3cb815df3793b3937b2e91d0fb00f1 refs/tags/2.0.0\n            bd533a768ff661991a689d3758fcfe72f455435d refs/tags/2.0.1\n            c0288a17899b2c6818f74e3a90b77e2a1779f96a refs/tags/2.0.2\n            a63a2de628a3ce89034b7d1a5ca5e8159534eef0 refs/tags/2.1.0.beta2\n            c7fc3d16ed9dc0b19f0d27583ca661a64562d21e refs/tags/2.1.0.rc1\n            edc0a2d02a0cc8eae8b67a3a275f65cd126c05b1 refs/tags/2.1.0.rc2\n\n        Into VCSTag objects with the tag name as verbose_name and the commit\n        hash as identifier.\n        \"\"\"\n        # parse the lines into a list of tuples (commit-hash, tag ref name)\n        # StringIO below is expecting Unicode data, so ensure that it gets it.\n        if not isinstance(data, str):\n            data = str(data)\n        delimiter = str(' ').encode('utf-8') if PY2 else str(' ')\n        raw_tags = csv.reader(StringIO(data), delimiter=delimiter)\n        vcs_tags = []\n        for row in raw_tags:\n            row = [f for f in row if f != '']\n            if row == []:\n                continue\n            commit_hash, name = row\n            clean_name = name.replace('refs/tags/', '')\n            vcs_tags.append(VCSVersion(self, commit_hash, clean_name))\n        return vcs_tags\n\n    @property\n    def branches(self):\n        # Only show remote branches\n        retcode, stdout, _ = self.run(\n            'git',\n            'branch',\n            '-r',\n            record_as_success=True,\n        )\n        # error (or no branches found)\n        if retcode != 0:\n            return []\n        return self.parse_branches(stdout)\n\n    def parse_branches(self, data):\n        \"\"\"\n        Parse output of git branch -r.\n\n        e.g.:\n\n              origin/2.0.X\n              origin/HEAD -> origin/master\n              origin/develop\n              origin/master\n              origin/release/2.0.0\n              origin/release/2.1.0\n        \"\"\"\n        clean_branches = []\n        # StringIO below is expecting Unicode data, so ensure that it gets it.\n        if not isinstance(data, str):\n            data = str(data)\n        delimiter = str(' ').encode('utf-8') if PY2 else str(' ')\n        raw_branches = csv.reader(StringIO(data), delimiter=delimiter)\n        for branch in raw_branches:\n            branch = [f for f in branch if f != '' and f != '*']\n            # Handle empty branches\n            if branch:\n                branch = branch[0]\n                if branch.startswith('origin/'):\n                    verbose_name = branch.replace('origin/', '')\n                    if verbose_name in ['HEAD']:\n                        continue\n                    clean_branches.append(\n                        VCSVersion(self, branch, verbose_name))\n                else:\n                    clean_branches.append(VCSVersion(self, branch, branch))\n        return clean_branches\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        self.check_working_dir()\n\n        # Clone or update repository\n        if self.repo_exists():\n            self.set_remote_url(self.repo_url)\n            self.fetch()\n        else:\n            self.make_clean_working_dir()\n            self.clone()\n\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\n        # Update submodules, temporarily allow for skipping submodule checkout\n        # step for projects need more submodule configuration.\n        if self.are_submodules_available():\n            if self.are_submodules_valid():\n                self.checkout_submodules()\n            else:\n                raise RepositoryError(RepositoryError.INVALID_SUBMODULES)\n        return code, out, err\n\n    def checkout_submodules(self):\n        \"\"\"Checkout all repository submodules recursively.\"\"\"\n        self.run('git', 'submodule', 'sync')\n        self.run(\n            'git',\n            'submodule',\n            'update',\n            '--init',\n            '--recursive',\n            '--force',\n        )\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        code, _, _ = self.run('git', 'show-ref', ref, record_as_success=True)\n        return code == 0\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, division, print_function, unicode_literals)\n\nimport csv\nimport logging\nimport os\nimport re\n\nimport git\nfrom django.core.exceptions import ValidationError\nfrom git.exc import BadName\nfrom six import PY2, StringIO\n\nfrom readthedocs.core.validators import validate_repository_url\nfrom readthedocs.projects.exceptions import RepositoryError\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    fallback_branch = 'master'  # default branch\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        # Use checkout() to update repo\n        self.checkout()\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):\n        \"\"\"\n        Test whether git submodule checkout step should be performed.\n\n        .. note::\n\n            Temporarily, we support skipping these steps as submodule step can\n            fail if using private submodules. This will eventually be\n            configureable with our YAML config.\n        \"\"\"\n        # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30\n        from readthedocs.projects.models import Feature\n        if self.project.has_feature(Feature.SKIP_SUBMODULES):\n            return False\n        code, out, _ = self.run('git', 'submodule', 'status', record=False)\n        return code == 0 and bool(out)\n\n    def are_submodules_valid(self):\n        \"\"\"Test that all submodule URLs are valid.\"\"\"\n        repo = git.Repo(self.working_dir)\n        for submodule in repo.submodules:\n            try:\n                validate_repository_url(submodule.url)\n            except ValidationError:\n                return False\n        return True\n\n    def fetch(self):\n        code, _, _ = self.run('git', 'fetch', '--tags', '--prune')\n        if code != 0:\n            raise RepositoryError\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        \"\"\"\n        Clone the repository.\n\n        .. note::\n\n            Temporarily, we support skipping submodule recursive clone via a\n            feature flag. This will eventually be configureable with our YAML\n            config.\n        \"\"\"\n        # TODO remove with https://github.com/rtfd/readthedocs-build/issues/30\n        from readthedocs.projects.models import Feature\n        cmd = ['git', 'clone']\n        if not self.project.has_feature(Feature.SKIP_SUBMODULES):\n            cmd.append('--recursive')\n        cmd.extend([self.repo_url, '.'])\n        code, _, _ = self.run(*cmd)\n        if code != 0:\n            raise RepositoryError\n\n    @property\n    def tags(self):\n        retcode, stdout, _ = self.run(\n            'git',\n            'show-ref',\n            '--tags',\n            record_as_success=True,\n        )\n        # error (or no tags found)\n        if retcode != 0:\n            return []\n        return self.parse_tags(stdout)\n\n    def parse_tags(self, data):\n        \"\"\"\n        Parses output of show-ref --tags, eg:\n\n            3b32886c8d3cb815df3793b3937b2e91d0fb00f1 refs/tags/2.0.0\n            bd533a768ff661991a689d3758fcfe72f455435d refs/tags/2.0.1\n            c0288a17899b2c6818f74e3a90b77e2a1779f96a refs/tags/2.0.2\n            a63a2de628a3ce89034b7d1a5ca5e8159534eef0 refs/tags/2.1.0.beta2\n            c7fc3d16ed9dc0b19f0d27583ca661a64562d21e refs/tags/2.1.0.rc1\n            edc0a2d02a0cc8eae8b67a3a275f65cd126c05b1 refs/tags/2.1.0.rc2\n\n        Into VCSTag objects with the tag name as verbose_name and the commit\n        hash as identifier.\n        \"\"\"\n        # parse the lines into a list of tuples (commit-hash, tag ref name)\n        # StringIO below is expecting Unicode data, so ensure that it gets it.\n        if not isinstance(data, str):\n            data = str(data)\n        delimiter = str(' ').encode('utf-8') if PY2 else str(' ')\n        raw_tags = csv.reader(StringIO(data), delimiter=delimiter)\n        vcs_tags = []\n        for row in raw_tags:\n            row = [f for f in row if f != '']\n            if row == []:\n                continue\n            commit_hash, name = row\n            clean_name = name.replace('refs/tags/', '')\n            vcs_tags.append(VCSVersion(self, commit_hash, clean_name))\n        return vcs_tags\n\n    @property\n    def branches(self):\n        # Only show remote branches\n        retcode, stdout, _ = self.run(\n            'git',\n            'branch',\n            '-r',\n            record_as_success=True,\n        )\n        # error (or no branches found)\n        if retcode != 0:\n            return []\n        return self.parse_branches(stdout)\n\n    def parse_branches(self, data):\n        \"\"\"\n        Parse output of git branch -r.\n\n        e.g.:\n\n              origin/2.0.X\n              origin/HEAD -> origin/master\n              origin/develop\n              origin/master\n              origin/release/2.0.0\n              origin/release/2.1.0\n        \"\"\"\n        clean_branches = []\n        # StringIO below is expecting Unicode data, so ensure that it gets it.\n        if not isinstance(data, str):\n            data = str(data)\n        delimiter = str(' ').encode('utf-8') if PY2 else str(' ')\n        raw_branches = csv.reader(StringIO(data), delimiter=delimiter)\n        for branch in raw_branches:\n            branch = [f for f in branch if f != '' and f != '*']\n            # Handle empty branches\n            if branch:\n                branch = branch[0]\n                if branch.startswith('origin/'):\n                    verbose_name = branch.replace('origin/', '')\n                    if verbose_name in ['HEAD']:\n                        continue\n                    clean_branches.append(\n                        VCSVersion(self, branch, verbose_name))\n                else:\n                    clean_branches.append(VCSVersion(self, branch, branch))\n        return clean_branches\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        self.check_working_dir()\n\n        # Clone or update repository\n        if self.repo_exists():\n            self.set_remote_url(self.repo_url)\n            self.fetch()\n        else:\n            self.make_clean_working_dir()\n            self.clone()\n\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\n        # Update submodules, temporarily allow for skipping submodule checkout\n        # step for projects need more submodule configuration.\n        if self.are_submodules_available():\n            if self.are_submodules_valid():\n                self.checkout_submodules()\n            else:\n                raise RepositoryError(RepositoryError.INVALID_SUBMODULES)\n        return code, out, err\n\n    def checkout_submodules(self):\n        \"\"\"Checkout all repository submodules recursively.\"\"\"\n        self.run('git', 'submodule', 'sync')\n        self.run(\n            'git',\n            'submodule',\n            'update',\n            '--init',\n            '--recursive',\n            '--force',\n        )\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"}]}