Skip to content

Commit f8f35ac

Browse files
committed
New git clone/fetch/checkout behavior first steps
1 parent de1c1fa commit f8f35ac

File tree

3 files changed

+86
-9
lines changed

3 files changed

+86
-9
lines changed

readthedocs/doc_builder/director.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ def setup_vcs(self):
9494
environment=self.vcs_environment,
9595
verbose_name=self.data.version.verbose_name,
9696
version_type=self.data.version.type,
97+
version_identifier=self.data.version.identifier,
9798
)
9899

99100
# We can't do too much on ``pre_checkout`` because we haven't

readthedocs/projects/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,7 @@ def add_features(sender, **kwargs):
19411941
INDEX_FROM_HTML_FILES = 'index_from_html_files'
19421942

19431943
# Build related features
1944+
GIT_CLONE_FETCH_CHECKOUT_PATTERN = "git_clone_fetch_checkout"
19441945
LIST_PACKAGES_INSTALLED_ENV = "list_packages_installed_env"
19451946
VCS_REMOTE_LISTING = "vcs_remote_listing"
19461947
SPHINX_PARALLEL = "sphinx_parallel"
@@ -2108,6 +2109,12 @@ def add_features(sender, **kwargs):
21082109
"versions"
21092110
),
21102111
),
2112+
(
2113+
GIT_CLONE_FETCH_CHECKOUT_PATTERN,
2114+
_(
2115+
"Build: Use simplified and optimized git clone + git fetch + git checkout patterns"
2116+
),
2117+
),
21112118
(
21122119
SPHINX_PARALLEL,
21132120
_('Sphinx: Use "-j auto" when calling sphinx-build'),

readthedocs/vcs_support/backends/git.py

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from git.exc import BadName, InvalidGitRepositoryError, NoSuchPathError
99
from gitdb.util import hex_to_bin
1010

11-
from readthedocs.builds.constants import EXTERNAL
11+
from readthedocs.builds.constants import BRANCH, EXTERNAL, TAG
1212
from readthedocs.config import ALL
1313
from readthedocs.projects.constants import (
1414
GITHUB_BRAND,
@@ -35,6 +35,7 @@ class Backend(BaseVCS):
3535
repo_depth = 50
3636

3737
def __init__(self, *args, **kwargs):
38+
self.version_identifier = kwargs.pop("version_identifier", None)
3839
super().__init__(*args, **kwargs)
3940
self.token = kwargs.get('token')
4041
self.repo_url = self._get_clone_url()
@@ -58,15 +59,76 @@ def set_remote_url(self, url):
5859
def update(self):
5960
"""Clone or update the repository."""
6061
super().update()
61-
if self.repo_exists():
62-
self.set_remote_url(self.repo_url)
63-
return self.fetch()
64-
self.make_clean_working_dir()
65-
# A fetch is always required to get external versions properly
62+
63+
if self.use_clone_fetch_checkout_pattern():
64+
65+
# New behavior: Clone is responsible for skipping the operation if the
66+
# repo is already cloned.
67+
self.clone_ng()
68+
# New behavior: No confusing return value. We are not using return values
69+
# in the callers.
70+
self.fetch_ng()
71+
72+
else:
73+
if self.repo_exists():
74+
self.set_remote_url(self.repo_url)
75+
return self.fetch()
76+
self.make_clean_working_dir()
77+
# A fetch is always required to get external versions properly
78+
if self.version_type == EXTERNAL:
79+
self.clone()
80+
return self.fetch()
81+
return self.clone()
82+
83+
def get_remote_reference(self):
84+
# Tags and branches have the tag/branch identifier set by the caller who instantiated the
85+
# Git backend -- this means that the build process needs to know this from build data,
86+
# essentially from an incoming webhook call.
87+
if self.version_type in (BRANCH, TAG):
88+
return self.version_identifier
6689
if self.version_type == EXTERNAL:
67-
self.clone()
68-
return self.fetch()
69-
return self.clone()
90+
git_provider_name = self.project.git_provider_name
91+
if git_provider_name == GITHUB_BRAND:
92+
return GITHUB_PR_PULL_PATTERN.format(id=self.verbose_name)
93+
if self.project.git_provider_name == GITLAB_BRAND:
94+
return GITLAB_MR_PULL_PATTERN
95+
96+
# This seems to be the default behavior when we don't know the remote
97+
# reference for a PR/MR: Just fetch everything
98+
return None
99+
100+
def clone_ng(self):
101+
# If the repository is already cloned, we don't do anything.
102+
# TODO: Why is it already cloned?
103+
if self.repo_exists():
104+
return
105+
106+
# --no-checkout: Makes it explicit what we are doing here. Nothing is checked out
107+
# until it's explcitly done.
108+
# --depth 1: Shallow clone, fetch as little data as possible.
109+
cmd = ["git", "clone", "--no-checkout", "--depth", "1"]
110+
111+
code, stdout, stderr = self.run(*cmd)
112+
return code, stdout, stderr
113+
114+
def fetch_ng(self):
115+
"""Implementation for new clone+fetch+checkout pattern."""
116+
117+
# --force: ?
118+
# --tags: We need to fetch tags in order to resolve these references in the checkout
119+
# --prune: ?
120+
# --prune-tags: ?
121+
cmd = ["git", "fetch", "origin", "--force", "--tags", "--prune", "--prune-tags"]
122+
remote_reference = self.get_remote_reference()
123+
124+
if remote_reference:
125+
# TODO: If we are fetching just one reference, what should the depth be?
126+
# A PR might have another commit added after the build has started...
127+
cmd.append("--depth 1")
128+
cmd.append(remote_reference)
129+
130+
code, stdout, stderr = self.run(*cmd)
131+
return code, stdout, stderr
70132

71133
def repo_exists(self):
72134
try:
@@ -150,6 +212,13 @@ def use_shallow_clone(self):
150212
from readthedocs.projects.models import Feature
151213
return not self.project.has_feature(Feature.DONT_SHALLOW_CLONE)
152214

215+
def use_clone_fetch_checkout_pattern(self):
216+
"""Use the new and optimized clone / fetch / checkout pattern."""
217+
from readthedocs.projects.models import Feature
218+
219+
# TODO: Remove the 'not'
220+
return not self.project.has_feature(Feature.GIT_CLONE_FETCH_CHECKOUT_PATTERN)
221+
153222
def fetch(self):
154223
# --force lets us checkout branches that are not fast-forwarded
155224
# https://github.com/readthedocs/readthedocs.org/issues/6097

0 commit comments

Comments
 (0)