summaryrefslogtreecommitdiffstats
path: root/git_superproject.py
diff options
context:
space:
mode:
authorRaman Tenneti <rtenneti@google.com>2021-02-04 14:39:38 -0800
committerRaman Tenneti <rtenneti@google.com>2021-02-07 22:25:38 +0000
commit1fd7bc24386dbba3a9454bb49c702a642f00e34c (patch)
treee02a94fb7e74785b5fc4687b26972d91a17b99b6 /git_superproject.py
parentb5c5a5e0688743b21c032287ae2b4357ec66f11d (diff)
downloadgit-repo-1fd7bc24386dbba3a9454bb49c702a642f00e34c.tar.gz
sync: superproject performance changes.
After updating all project’s revsionIds with the SHAs from superproject, write the updated manifest into superproject_override.xml file. Reload that file for future Reloads. This file is created in exp-superproject directory. Moved most of the code that is superproject specific into git_superproject.py and wrote test code. If git pull fails, did a git clone of the superproject. We saw performance gains for consecutive repo sync's. The time to sync went down from around 120 secs to 40 secs when repo sync is executed consecutively. Tested the code with the following commands. $ ./run_tests -v tests/test_git_superproject.py $ ./run_tests -v Tested the sync code by copying all the repo changes into my Android AOSP checkout and doing a repo sync --use-superproject twice. First run $ time repo sync --use-superproject ... real 21m3.745s user 97m59.380s sys 19m11.286s After two consecutive sync runs $ time repo sync -c -j8 --use-superproject real 0m39.626s user 0m29.937s sys 0m38.155s Bug: https://crbug.com/gerrit/13709 Bug: https://crbug.com/gerrit/13707 Tested-by: Raman Tenneti <rtenneti@google.com> Change-Id: Id79a0d7c4d20babd65e9bd485196c6f8fbe9de5e Reviewed-on: https://gerrit-review.googlesource.com/c/git-repo/+/296082 Reviewed-by: Ian Kasprzak <iankaz@google.com> Tested-by: Raman Tenneti <rtenneti@google.com>
Diffstat (limited to 'git_superproject.py')
-rw-r--r--git_superproject.py100
1 files changed, 85 insertions, 15 deletions
diff --git a/git_superproject.py b/git_superproject.py
index e2045cfd..465d1f87 100644
--- a/git_superproject.py
+++ b/git_superproject.py
@@ -25,8 +25,9 @@ Examples:
25import os 25import os
26import sys 26import sys
27 27
28from error import GitError 28from error import BUG_REPORT_URL, GitError
29from git_command import GitCommand 29from git_command import GitCommand
30import platform_utils
30 31
31 32
32class Superproject(object): 33class Superproject(object):
@@ -46,6 +47,9 @@ class Superproject(object):
46 self._repodir = os.path.abspath(repodir) 47 self._repodir = os.path.abspath(repodir)
47 self._superproject_dir = superproject_dir 48 self._superproject_dir = superproject_dir
48 self._superproject_path = os.path.join(self._repodir, superproject_dir) 49 self._superproject_path = os.path.join(self._repodir, superproject_dir)
50 self._manifest_path = os.path.join(self._superproject_path,
51 'superproject_override.xml')
52 self._work_git = os.path.join(self._superproject_path, 'superproject')
49 53
50 @property 54 @property
51 def project_shas(self): 55 def project_shas(self):
@@ -57,7 +61,7 @@ class Superproject(object):
57 61
58 Args: 62 Args:
59 url: superproject's url to be passed to git clone. 63 url: superproject's url to be passed to git clone.
60 branch: the branchname to be passed as argument to git clone. 64 branch: The branchname to be passed as argument to git clone.
61 65
62 Returns: 66 Returns:
63 True if 'git clone <url> <branch>' is successful, or False. 67 True if 'git clone <url> <branch>' is successful, or False.
@@ -86,13 +90,12 @@ class Superproject(object):
86 Returns: 90 Returns:
87 True if 'git pull <branch>' is successful, or False. 91 True if 'git pull <branch>' is successful, or False.
88 """ 92 """
89 git_dir = os.path.join(self._superproject_path, 'superproject') 93 if not os.path.exists(self._work_git):
90 if not os.path.exists(git_dir): 94 raise GitError('git pull missing drectory: %s' % self._work_git)
91 raise GitError('git pull. Missing drectory: %s' % git_dir)
92 cmd = ['pull'] 95 cmd = ['pull']
93 p = GitCommand(None, 96 p = GitCommand(None,
94 cmd, 97 cmd,
95 cwd=git_dir, 98 cwd=self._work_git,
96 capture_stdout=True, 99 capture_stdout=True,
97 capture_stderr=True) 100 capture_stderr=True)
98 retval = p.Wait() 101 retval = p.Wait()
@@ -110,14 +113,13 @@ class Superproject(object):
110 Returns: 113 Returns:
111 data: data returned from 'git ls-tree -r HEAD' instead of None. 114 data: data returned from 'git ls-tree -r HEAD' instead of None.
112 """ 115 """
113 git_dir = os.path.join(self._superproject_path, 'superproject') 116 if not os.path.exists(self._work_git):
114 if not os.path.exists(git_dir): 117 raise GitError('git ls-tree. Missing drectory: %s' % self._work_git)
115 raise GitError('git ls-tree. Missing drectory: %s' % git_dir)
116 data = None 118 data = None
117 cmd = ['ls-tree', '-z', '-r', 'HEAD'] 119 cmd = ['ls-tree', '-z', '-r', 'HEAD']
118 p = GitCommand(None, 120 p = GitCommand(None,
119 cmd, 121 cmd,
120 cwd=git_dir, 122 cwd=self._work_git,
121 capture_stdout=True, 123 capture_stdout=True,
122 capture_stderr=True) 124 capture_stderr=True)
123 retval = p.Wait() 125 retval = p.Wait()
@@ -130,22 +132,26 @@ class Superproject(object):
130 retval, p.stderr), file=sys.stderr) 132 retval, p.stderr), file=sys.stderr)
131 return data 133 return data
132 134
133 def GetAllProjectsSHAs(self, url, branch=None): 135 def _GetAllProjectsSHAs(self, url, branch=None):
134 """Get SHAs for all projects from superproject and save them in _project_shas. 136 """Get SHAs for all projects from superproject and save them in _project_shas.
135 137
136 Args: 138 Args:
137 url: superproject's url to be passed to git clone. 139 url: superproject's url to be passed to git clone or pull.
138 branch: the branchname to be passed as argument to git clone. 140 branch: The branchname to be passed as argument to git clone or pull.
139 141
140 Returns: 142 Returns:
141 A dictionary with the projects/SHAs instead of None. 143 A dictionary with the projects/SHAs instead of None.
142 """ 144 """
143 if not url: 145 if not url:
144 raise ValueError('url argument is not supplied.') 146 raise ValueError('url argument is not supplied.')
147 do_clone = True
145 if os.path.exists(self._superproject_path): 148 if os.path.exists(self._superproject_path):
146 if not self._Pull(): 149 if not self._Pull():
147 raise GitError('git pull failed for url: %s' % url) 150 # If pull fails due to a corrupted git directory, then do a git clone.
148 else: 151 platform_utils.rmtree(self._superproject_path)
152 else:
153 do_clone = False
154 if do_clone:
149 if not self._Clone(url, branch): 155 if not self._Clone(url, branch):
150 raise GitError('git clone failed for url: %s' % url) 156 raise GitError('git clone failed for url: %s' % url)
151 157
@@ -168,3 +174,67 @@ class Superproject(object):
168 174
169 self._project_shas = shas 175 self._project_shas = shas
170 return shas 176 return shas
177
178 def _WriteManfiestFile(self, manifest):
179 """Writes manifest to a file.
180
181 Args:
182 manifest: A Manifest object that is to be written to a file.
183
184 Returns:
185 manifest_path: Path name of the file into which manifest is written instead of None.
186 """
187 if not os.path.exists(self._superproject_path):
188 print('error: missing superproject directory %s' %
189 self._superproject_path,
190 file=sys.stderr)
191 return None
192 manifest_str = manifest.ToXml().toxml()
193 manifest_path = self._manifest_path
194 try:
195 with open(manifest_path, 'w', encoding='utf-8') as fp:
196 fp.write(manifest_str)
197 except IOError as e:
198 print('error: cannot write manifest to %s:\n%s'
199 % (manifest_path, e),
200 file=sys.stderr)
201 return None
202 return manifest_path
203
204 def UpdateProjectsRevisionId(self, manifest, projects, url, branch=None):
205 """Update revisionId of every project in projects with the SHA.
206
207 Args:
208 manifest: A Manifest object that is to be written to a file.
209 projects: List of projects whose revisionId needs to be updated.
210 url: superproject's url to be passed to git clone or fetch.
211 branch: The branchname to be passed as argument to git clone or pull.
212
213 Returns:
214 manifest_path: Path name of the overriding manfiest file instead of None.
215 """
216 try:
217 shas = self._GetAllProjectsSHAs(url=url, branch=branch)
218 except Exception as e:
219 print('error: Cannot get project SHAs for %s: %s: %s' %
220 (url, type(e).__name__, str(e)),
221 file=sys.stderr)
222 return None
223
224 projects_missing_shas = []
225 for project in projects:
226 path = project.relpath
227 if not path:
228 continue
229 sha = shas.get(path)
230 if sha:
231 project.SetRevisionId(sha)
232 else:
233 projects_missing_shas.append(path)
234 if projects_missing_shas:
235 print('error: please file a bug using %s to report missing shas for: %s' %
236 (BUG_REPORT_URL, projects_missing_shas), file=sys.stderr)
237 return None
238
239 manifest_path = self._WriteManfiestFile(manifest)
240 return manifest_path