blob: 1e31a20a324429e61fd6dc6477962b143d93e1cd [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080015import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070016import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070017import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070019import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import re
21import shutil
22import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070023import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020025import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080026import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.parse
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070029
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070031from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070032from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
33 ID_RE
Remy Bohmer16c13282020-09-10 10:38:04 +020034from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040035from error import ManifestInvalidRevisionError, ManifestInvalidPathError
Conley Owens75ee0572012-11-15 17:33:11 -080036from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070037import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040038import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040039from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070043
George Engelbrecht9bc283e2020-04-02 12:36:09 -060044# Maximum sleep time allowed during retries.
45MAXIMUM_RETRY_SLEEP_SEC = 3600.0
46# +-10% random jitter is added to each Fetches retry sleep duration.
47RETRY_JITTER_PERCENT = 0.1
48
49
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070050def _lwrite(path, content):
51 lock = '%s.lock' % path
52
Remy Bohmer169b0212020-11-21 10:57:52 +010053 # Maintain Unix line endings on all OS's to match git behavior.
54 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056
57 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070058 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080060 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 raise
62
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070063
Shawn O. Pearce48244782009-04-16 08:25:57 -070064def _error(fmt, *args):
65 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070066 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070067
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070068
David Pursehousef33929d2015-08-24 14:39:14 +090069def _warn(fmt, *args):
70 msg = fmt % args
71 print('warn: %s' % msg, file=sys.stderr)
72
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070073
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070074def not_rev(r):
75 return '^' + r
76
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080078def sq(r):
79 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080080
David Pursehouse819827a2020-02-12 15:20:19 +090081
Jonathan Nieder93719792015-03-17 11:29:58 -070082_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
84
Jonathan Nieder93719792015-03-17 11:29:58 -070085def _ProjectHooks():
86 """List the hooks present in the 'hooks' directory.
87
88 These hooks are project hooks and are copied to the '.git/hooks' directory
89 of all subprojects.
90
91 This function caches the list of hooks (based on the contents of the
92 'repo/hooks' directory) on the first call.
93
94 Returns:
95 A list of absolute paths to all of the files in the hooks directory.
96 """
97 global _project_hook_list
98 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -070099 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700100 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700101 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700102 return _project_hook_list
103
104
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700105class DownloadedChange(object):
106 _commit_cache = None
107
108 def __init__(self, project, base, change_id, ps_id, commit):
109 self.project = project
110 self.base = base
111 self.change_id = change_id
112 self.ps_id = ps_id
113 self.commit = commit
114
115 @property
116 def commits(self):
117 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700118 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
119 '--abbrev-commit',
120 '--pretty=oneline',
121 '--reverse',
122 '--date-order',
123 not_rev(self.base),
124 self.commit,
125 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700126 return self._commit_cache
127
128
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700129class ReviewableBranch(object):
130 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400131 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700132
133 def __init__(self, project, branch, base):
134 self.project = project
135 self.branch = branch
136 self.base = base
137
138 @property
139 def name(self):
140 return self.branch.name
141
142 @property
143 def commits(self):
144 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400145 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
146 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
147 try:
148 self._commit_cache = self.project.bare_git.rev_list(*args)
149 except GitError:
150 # We weren't able to probe the commits for this branch. Was it tracking
151 # a branch that no longer exists? If so, return no commits. Otherwise,
152 # rethrow the error as we don't know what's going on.
153 if self.base_exists:
154 raise
155
156 self._commit_cache = []
157
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mike Frysinger6da17752019-09-11 18:43:17 -0400176 @property
177 def base_exists(self):
178 """Whether the branch we're tracking exists.
179
180 Normally it should, but sometimes branches we track can get deleted.
181 """
182 if self._base_exists is None:
183 try:
184 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
185 # If we're still here, the base branch exists.
186 self._base_exists = True
187 except GitError:
188 # If we failed to verify, the base branch doesn't exist.
189 self._base_exists = False
190
191 return self._base_exists
192
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700193 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500194 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700195 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500196 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500197 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200198 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700199 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200200 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200201 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800202 validate_certs=True,
203 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500204 self.project.UploadForReview(branch=self.name,
205 people=people,
206 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700207 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500208 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500209 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200210 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700211 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200213 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800214 validate_certs=validate_certs,
215 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700217 def GetPublishedRefs(self):
218 refs = {}
219 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700220 self.branch.remote.SshReviewUrl(self.project.UserEmail),
221 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700222 for line in output.split('\n'):
223 try:
224 (sha, ref) = line.split()
225 refs[sha] = ref
226 except ValueError:
227 pass
228
229 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500235 super().__init__(config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100236 self.project = self.printer('header', attr='bold')
237 self.branch = self.printer('header', attr='bold')
238 self.nobranch = self.printer('nobranch', fg='red')
239 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Anthony King7bdac712014-07-16 12:56:40 +0100241 self.added = self.printer('added', fg='green')
242 self.changed = self.printer('changed', fg='red')
243 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244
245
246class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700247
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500249 super().__init__(config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100250 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400251 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700253
Anthony King7bdac712014-07-16 12:56:40 +0100254class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700255
James W. Mills24c13082012-04-12 15:04:13 -0500256 def __init__(self, name, value, keep):
257 self.name = name
258 self.value = value
259 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700261
Mike Frysingere6a202f2019-08-02 15:57:57 -0400262def _SafeExpandPath(base, subpath, skipfinal=False):
263 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Mike Frysingere6a202f2019-08-02 15:57:57 -0400265 We make sure no intermediate symlinks are traversed, and that the final path
266 is not a special file (e.g. not a socket or fifo).
267
268 NB: We rely on a number of paths already being filtered out while parsing the
269 manifest. See the validation logic in manifest_xml.py for more details.
270 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500271 # Split up the path by its components. We can't use os.path.sep exclusively
272 # as some platforms (like Windows) will convert / to \ and that bypasses all
273 # our constructed logic here. Especially since manifest authors only use
274 # / in their paths.
275 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
276 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400277 if skipfinal:
278 # Whether the caller handles the final component itself.
279 finalpart = components.pop()
280
281 path = base
282 for part in components:
283 if part in {'.', '..'}:
284 raise ManifestInvalidPathError(
285 '%s: "%s" not allowed in paths' % (subpath, part))
286
287 path = os.path.join(path, part)
288 if platform_utils.islink(path):
289 raise ManifestInvalidPathError(
290 '%s: traversing symlinks not allow' % (path,))
291
292 if os.path.exists(path):
293 if not os.path.isfile(path) and not platform_utils.isdir(path):
294 raise ManifestInvalidPathError(
295 '%s: only regular files & directories allowed' % (path,))
296
297 if skipfinal:
298 path = os.path.join(path, finalpart)
299
300 return path
301
302
303class _CopyFile(object):
304 """Container for <copyfile> manifest element."""
305
306 def __init__(self, git_worktree, src, topdir, dest):
307 """Register a <copyfile> request.
308
309 Args:
310 git_worktree: Absolute path to the git project checkout.
311 src: Relative path under |git_worktree| of file to read.
312 topdir: Absolute path to the top of the repo client checkout.
313 dest: Relative path under |topdir| of file to write.
314 """
315 self.git_worktree = git_worktree
316 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700317 self.src = src
318 self.dest = dest
319
320 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400321 src = _SafeExpandPath(self.git_worktree, self.src)
322 dest = _SafeExpandPath(self.topdir, self.dest)
323
324 if platform_utils.isdir(src):
325 raise ManifestInvalidPathError(
326 '%s: copying from directory not supported' % (self.src,))
327 if platform_utils.isdir(dest):
328 raise ManifestInvalidPathError(
329 '%s: copying to directory not allowed' % (self.dest,))
330
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700331 # copy file if it does not exist or is out of date
332 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
333 try:
334 # remove existing file first, since it might be read-only
335 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800336 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400337 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200338 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700339 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200340 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341 shutil.copy(src, dest)
342 # make the file read-only
343 mode = os.stat(dest)[stat.ST_MODE]
344 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
345 os.chmod(dest, mode)
346 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700347 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Anthony King7bdac712014-07-16 12:56:40 +0100350class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400351 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700352
Mike Frysingere6a202f2019-08-02 15:57:57 -0400353 def __init__(self, git_worktree, src, topdir, dest):
354 """Register a <linkfile> request.
355
356 Args:
357 git_worktree: Absolute path to the git project checkout.
358 src: Target of symlink relative to path under |git_worktree|.
359 topdir: Absolute path to the top of the repo client checkout.
360 dest: Relative path under |topdir| of symlink to create.
361 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700362 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400363 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500364 self.src = src
365 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500366
Wink Saville4c426ef2015-06-03 08:05:17 -0700367 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500368 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700369 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500370 try:
371 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800372 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800373 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500374 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700375 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700376 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500377 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700378 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500379 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700380 _error('Cannot link file %s to %s', relSrc, absDest)
381
382 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400383 """Link the self.src & self.dest paths.
384
385 Handles wild cards on the src linking all of the files in the source in to
386 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700387 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500388 # Some people use src="." to create stable links to projects. Lets allow
389 # that but reject all other uses of "." to keep things simple.
390 if self.src == '.':
391 src = self.git_worktree
392 else:
393 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400394
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300395 if not glob.has_magic(src):
396 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400397 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
398 # dest & src are absolute paths at this point. Make sure the target of
399 # the symlink is relative in the context of the repo client checkout.
400 relpath = os.path.relpath(src, os.path.dirname(dest))
401 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700402 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400403 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300404 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400405 if os.path.exists(dest) and not platform_utils.isdir(dest):
406 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700407 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400408 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700409 # Create a releative path from source dir to destination dir
410 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400411 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700412
413 # Get the source file name
414 srcFile = os.path.basename(absSrcFile)
415
416 # Now form the final full paths to srcFile. They will be
417 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400418 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700419 relSrc = os.path.join(relSrcDir, srcFile)
420 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500421
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700422
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700423class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700424
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700425 def __init__(self,
426 name,
Anthony King7bdac712014-07-16 12:56:40 +0100427 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700428 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100429 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700430 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700431 orig_name=None,
432 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700433 self.name = name
434 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700435 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700436 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100437 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700438 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700439 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700440
Ian Kasprzak0286e312021-02-05 10:06:18 -0800441
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700442class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600443 # These objects can be shared between several working trees.
444 shareable_files = ['description', 'info']
445 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
446 # These objects can only be used by a single working tree.
447 working_tree_files = ['config', 'packed-refs', 'shallow']
448 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 def __init__(self,
451 manifest,
452 name,
453 remote,
454 gitdir,
David James8d201162013-10-11 17:03:19 -0700455 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456 worktree,
457 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700458 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800459 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100460 rebase=True,
461 groups=None,
462 sync_c=False,
463 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900464 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100465 clone_depth=None,
466 upstream=None,
467 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500468 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100469 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900470 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700471 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600472 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700473 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800474 """Init a Project object.
475
476 Args:
477 manifest: The XmlManifest object.
478 name: The `name` attribute of manifest.xml's project element.
479 remote: RemoteSpec object specifying its remote's properties.
480 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700481 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800482 worktree: Absolute path of git working tree.
483 relpath: Relative path of git working tree to repo's top directory.
484 revisionExpr: The `revision` attribute of manifest.xml's project element.
485 revisionId: git commit id for checking out.
486 rebase: The `rebase` attribute of manifest.xml's project element.
487 groups: The `groups` attribute of manifest.xml's project element.
488 sync_c: The `sync-c` attribute of manifest.xml's project element.
489 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900490 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800491 upstream: The `upstream` attribute of manifest.xml's project element.
492 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500493 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800494 is_derived: False if the project was explicitly defined in the manifest;
495 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400496 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900497 optimized_fetch: If True, when a project is set to a sha1 revision, only
498 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600499 retry_fetches: Retry remote fetches n times upon receiving transient error
500 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700501 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800502 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400503 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700504 self.name = name
505 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800506 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700507 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800508 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700509 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800510 else:
511 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700512 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700513 self.revisionExpr = revisionExpr
514
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700515 if revisionId is None \
516 and revisionExpr \
517 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700518 self.revisionId = revisionExpr
519 else:
520 self.revisionId = revisionId
521
Mike Pontillod3153822012-02-28 11:53:24 -0800522 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700523 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700524 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800525 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900526 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900527 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700528 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800529 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500530 # NB: Do not use this setting in __init__ to change behavior so that the
531 # manifest.git checkout can inspect & change it after instantiating. See
532 # the XmlManifest init code for more info.
533 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800534 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900535 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600536 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800537 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800538
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700539 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700540 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500541 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500542 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700543 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400544 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700545
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800546 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700547 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800548 else:
549 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700550 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700551 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700552 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400553 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700554 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555
Doug Anderson37282b42011-03-04 11:54:18 -0800556 # This will be filled in if a project is later identified to be the
557 # project containing repo hooks.
558 self.enabled_repo_hooks = []
559
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700560 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800561 def Derived(self):
562 return self.is_derived
563
564 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700566 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700567
568 @property
569 def CurrentBranch(self):
570 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400571
572 The branch name omits the 'refs/heads/' prefix.
573 None is returned if the project is on a detached HEAD, or if the work_git is
574 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400576 try:
577 b = self.work_git.GetHead()
578 except NoManifestException:
579 # If the local checkout is in a bad state, don't barf. Let the callers
580 # process this like the head is unreadable.
581 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 if b.startswith(R_HEADS):
583 return b[len(R_HEADS):]
584 return None
585
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700586 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500587 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
588 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
589 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200590
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591 def IsDirty(self, consider_untracked=True):
592 """Is the working directory modified in some way?
593 """
594 self.work_git.update_index('-q',
595 '--unmerged',
596 '--ignore-missing',
597 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900598 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599 return True
600 if self.work_git.DiffZ('diff-files'):
601 return True
602 if consider_untracked and self.work_git.LsOthers():
603 return True
604 return False
605
606 _userident_name = None
607 _userident_email = None
608
609 @property
610 def UserName(self):
611 """Obtain the user's personal name.
612 """
613 if self._userident_name is None:
614 self._LoadUserIdentity()
615 return self._userident_name
616
617 @property
618 def UserEmail(self):
619 """Obtain the user's email address. This is very likely
620 to be their Gerrit login.
621 """
622 if self._userident_email is None:
623 self._LoadUserIdentity()
624 return self._userident_email
625
626 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900627 u = self.bare_git.var('GIT_COMMITTER_IDENT')
628 m = re.compile("^(.*) <([^>]*)> ").match(u)
629 if m:
630 self._userident_name = m.group(1)
631 self._userident_email = m.group(2)
632 else:
633 self._userident_name = ''
634 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700635
636 def GetRemote(self, name):
637 """Get the configuration for a single remote.
638 """
639 return self.config.GetRemote(name)
640
641 def GetBranch(self, name):
642 """Get the configuration for a single branch.
643 """
644 return self.config.GetBranch(name)
645
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700646 def GetBranches(self):
647 """Get all existing local branches.
648 """
649 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900650 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700651 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700652
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530653 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700654 if name.startswith(R_HEADS):
655 name = name[len(R_HEADS):]
656 b = self.GetBranch(name)
657 b.current = name == current
658 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900659 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700660 heads[name] = b
661
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530662 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700663 if name.startswith(R_PUB):
664 name = name[len(R_PUB):]
665 b = heads.get(name)
666 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900667 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700668
669 return heads
670
Colin Cross5acde752012-03-28 20:15:45 -0700671 def MatchesGroups(self, manifest_groups):
672 """Returns true if the manifest groups specified at init should cause
673 this project to be synced.
674 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700675 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700676
677 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700678 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700679 manifest_groups: "-group1,group2"
680 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500681
682 The special manifest group "default" will match any project that
683 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700684 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500685 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700686 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700687 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500688 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700689
Conley Owens971de8e2012-04-16 10:36:08 -0700690 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700691 for group in expanded_manifest_groups:
692 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700693 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700694 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700695 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700696
Conley Owens971de8e2012-04-16 10:36:08 -0700697 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700699# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700700 def UncommitedFiles(self, get_all=True):
701 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700703 Args:
704 get_all: a boolean, if True - get information about all different
705 uncommitted files. If False - return as soon as any kind of
706 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500707 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700708 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500709 self.work_git.update_index('-q',
710 '--unmerged',
711 '--ignore-missing',
712 '--refresh')
713 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700714 details.append("rebase in progress")
715 if not get_all:
716 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500717
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700718 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
719 if changes:
720 details.extend(changes)
721 if not get_all:
722 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500723
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 changes = self.work_git.DiffZ('diff-files').keys()
725 if changes:
726 details.extend(changes)
727 if not get_all:
728 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500729
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700730 changes = self.work_git.LsOthers()
731 if changes:
732 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500733
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 return details
735
736 def HasChanges(self):
737 """Returns true if there are uncommitted changes.
738 """
739 if self.UncommitedFiles(get_all=False):
740 return True
741 else:
742 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500743
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600744 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200746
747 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200748 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600749 quiet: If True then only print the project name. Do not print
750 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700752 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700753 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200754 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700755 print(file=output_redir)
756 print('project %s/' % self.relpath, file=output_redir)
757 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 return
759
760 self.work_git.update_index('-q',
761 '--unmerged',
762 '--ignore-missing',
763 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700764 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
766 df = self.work_git.DiffZ('diff-files')
767 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100768 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700769 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700770
771 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700772 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200773 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700774 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600776 if quiet:
777 out.nl()
778 return 'DIRTY'
779
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 branch = self.CurrentBranch
781 if branch is None:
782 out.nobranch('(*** NO BRANCH ***)')
783 else:
784 out.branch('branch %s', branch)
785 out.nl()
786
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700787 if rb:
788 out.important('prior sync failed; rebase still in progress')
789 out.nl()
790
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791 paths = list()
792 paths.extend(di.keys())
793 paths.extend(df.keys())
794 paths.extend(do)
795
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530796 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900797 try:
798 i = di[p]
799 except KeyError:
800 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900802 try:
803 f = df[p]
804 except KeyError:
805 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200806
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900807 if i:
808 i_status = i.status.upper()
809 else:
810 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900812 if f:
813 f_status = f.status.lower()
814 else:
815 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700816
817 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800818 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700819 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820 else:
821 line = ' %s%s\t%s' % (i_status, f_status, p)
822
823 if i and not f:
824 out.added('%s', line)
825 elif (i and f) or (not i and f):
826 out.changed('%s', line)
827 elif not i and not f:
828 out.untracked('%s', line)
829 else:
830 out.write('%s', line)
831 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200832
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700833 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500835 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 """Prints the status of the repository to stdout.
837 """
838 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500839 if output_redir:
840 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700841 cmd = ['diff']
842 if out.is_on:
843 cmd.append('--color')
844 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300845 if absolute_paths:
846 cmd.append('--src-prefix=a/%s/' % self.relpath)
847 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400849 try:
850 p = GitCommand(self,
851 cmd,
852 capture_stdout=True,
853 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500854 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400855 except GitError as e:
856 out.nl()
857 out.project('project %s/' % self.relpath)
858 out.nl()
859 out.fail('%s', str(e))
860 out.nl()
861 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500862 if p.stdout:
863 out.nl()
864 out.project('project %s/' % self.relpath)
865 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500866 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400867 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700869# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900870 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700871 """Was the branch published (uploaded) for code review?
872 If so, returns the SHA-1 hash of the last published
873 state for the branch.
874 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700875 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900876 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700877 try:
878 return self.bare_git.rev_parse(key)
879 except GitError:
880 return None
881 else:
882 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900883 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700884 except KeyError:
885 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886
David Pursehouse8a68ff92012-09-24 12:15:13 +0900887 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888 """Prunes any stale published refs.
889 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900890 if all_refs is None:
891 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892 heads = set()
893 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530894 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700895 if name.startswith(R_HEADS):
896 heads.add(name)
897 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900898 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700899
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530900 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 n = name[len(R_PUB):]
902 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900903 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700905 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906 """List any branches which can be uploaded for review.
907 """
908 heads = {}
909 pubed = {}
910
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530911 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700912 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900913 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900915 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916
917 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530918 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900919 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700921 if selected_branch and branch != selected_branch:
922 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800924 rb = self.GetUploadableBranch(branch)
925 if rb:
926 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 return ready
928
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800929 def GetUploadableBranch(self, branch_name):
930 """Get a single uploadable branch, or None.
931 """
932 branch = self.GetBranch(branch_name)
933 base = branch.LocalMerge
934 if branch.LocalMerge:
935 rb = ReviewableBranch(self, branch, base)
936 if rb.commits:
937 return rb
938 return None
939
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700940 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100941 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500942 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700943 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500944 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500945 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200946 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700947 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200948 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200949 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800950 validate_certs=True,
951 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952 """Uploads the named branch for code review.
953 """
954 if branch is None:
955 branch = self.CurrentBranch
956 if branch is None:
957 raise GitError('not currently on a branch')
958
959 branch = self.GetBranch(branch)
960 if not branch.LocalMerge:
961 raise GitError('branch %s does not track a remote' % branch.name)
962 if not branch.remote.review:
963 raise GitError('remote %s has no review url' % branch.remote.name)
964
Bryan Jacobsf609f912013-05-06 13:36:24 -0400965 if dest_branch is None:
966 dest_branch = self.dest_branch
967 if dest_branch is None:
968 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700969 if not dest_branch.startswith(R_HEADS):
970 dest_branch = R_HEADS + dest_branch
971
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800972 if not branch.remote.projectname:
973 branch.remote.projectname = self.name
974 branch.remote.Save()
975
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200976 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800977 if url is None:
978 raise UploadError('review not configured')
979 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500980 if dryrun:
981 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800982
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800983 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -0800984 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700985
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800986 for push_option in (push_options or []):
987 cmd.append('-o')
988 cmd.append(push_option)
989
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800990 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800991
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800992 if dest_branch.startswith(R_HEADS):
993 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -0700994
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -0500995 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -0800996 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800997 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -0800998 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -0500999 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001000 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001001
David Pursehousef25a3702018-11-14 19:01:22 -08001002 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001003 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001004 if notify:
1005 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001006 if private:
1007 opts += ['private']
1008 if wip:
1009 opts += ['wip']
1010 if opts:
1011 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001012 cmd.append(ref_spec)
1013
Anthony King7bdac712014-07-16 12:56:40 +01001014 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001015 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016
Mike Frysingerd7f86832020-11-19 19:18:46 -05001017 if not dryrun:
1018 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1019 self.bare_git.UpdateRef(R_PUB + branch.name,
1020 R_HEADS + branch.name,
1021 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001023# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001024 def _ExtractArchive(self, tarpath, path=None):
1025 """Extract the given tar on its current location
1026
1027 Args:
1028 - tarpath: The path to the actual tar file
1029
1030 """
1031 try:
1032 with tarfile.open(tarpath, 'r') as tar:
1033 tar.extractall(path=path)
1034 return True
1035 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001036 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001037 return False
1038
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001039 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001040 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001041 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001042 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001043 is_new=None,
1044 current_branch_only=False,
1045 force_sync=False,
1046 clone_bundle=True,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001047 tags=True,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001048 archive=False,
1049 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001050 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001051 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001052 submodules=False,
Raman Tennetif32f2432021-04-12 20:57:25 -07001053 clone_filter=None,
1054 partial_clone_exclude=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001055 """Perform only the network IO portion of the sync process.
1056 Local working directory/branch state is not affected.
1057 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001058 if archive and not isinstance(self, MetaProject):
1059 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001060 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001061 return False
1062
1063 name = self.relpath.replace('\\', '/')
1064 name = name.replace('/', '_')
1065 tarpath = '%s.tar' % name
1066 topdir = self.manifest.topdir
1067
1068 try:
1069 self._FetchArchive(tarpath, cwd=topdir)
1070 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001071 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001072 return False
1073
1074 # From now on, we only need absolute tarpath
1075 tarpath = os.path.join(topdir, tarpath)
1076
1077 if not self._ExtractArchive(tarpath, path=topdir):
1078 return False
1079 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001080 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001081 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001082 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001083 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001084 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001085
1086 # If the shared object dir already exists, don't try to rebootstrap with a
1087 # clone bundle download. We should have the majority of objects already.
1088 if clone_bundle and os.path.exists(self.objdir):
1089 clone_bundle = False
1090
Raman Tennetif32f2432021-04-12 20:57:25 -07001091 if self.name in partial_clone_exclude:
1092 clone_bundle = True
1093 clone_filter = None
1094
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001095 if is_new is None:
1096 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001097 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001098 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001099 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001100 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001102
1103 if is_new:
1104 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1105 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001106 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001107 # This works for both absolute and relative alternate directories.
1108 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001109 except IOError:
1110 alt_dir = None
1111 else:
1112 alt_dir = None
1113
Mike Frysingere50b6a72020-02-19 01:45:48 -05001114 if (clone_bundle
1115 and alt_dir is None
1116 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001117 is_new = False
1118
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001119 if not current_branch_only:
1120 if self.sync_c:
1121 current_branch_only = True
1122 elif not self.manifest._loaded:
1123 # Manifest cannot check defaults until it syncs.
1124 current_branch_only = False
1125 elif self.manifest.default.sync_c:
1126 current_branch_only = True
1127
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001128 if not self.sync_tags:
1129 tags = False
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001130
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001131 if self.clone_depth:
1132 depth = self.clone_depth
1133 else:
1134 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1135
Mike Frysinger521d01b2020-02-17 01:51:49 -05001136 # See if we can skip the network fetch entirely.
1137 if not (optimized_fetch and
1138 (ID_RE.match(self.revisionExpr) and
1139 self._CheckForImmutableRevision())):
1140 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001141 initial=is_new,
1142 quiet=quiet, verbose=verbose, output_redir=output_redir,
1143 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001144 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001145 submodules=submodules, force_sync=force_sync,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001146 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001147 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001148
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001149 mp = self.manifest.manifestProject
1150 dissociate = mp.config.GetBoolean('repo.dissociate')
1151 if dissociate:
1152 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1153 if os.path.exists(alternates_file):
1154 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001155 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1156 merge_output=bool(output_redir))
1157 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001158 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001159 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001160 return False
1161 platform_utils.remove(alternates_file)
1162
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001163 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001164 self._InitMRef()
1165 else:
1166 self._InitMirrorHead()
1167 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001168 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001169 except OSError:
1170 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001171 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001172
1173 def PostRepoUpgrade(self):
1174 self._InitHooks()
1175
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001176 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001177 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001178 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001179 for copyfile in self.copyfiles:
1180 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001181 for linkfile in self.linkfiles:
1182 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001183
Julien Camperguedd654222014-01-09 16:21:37 +01001184 def GetCommitRevisionId(self):
1185 """Get revisionId of a commit.
1186
1187 Use this method instead of GetRevisionId to get the id of the commit rather
1188 than the id of the current git object (for example, a tag)
1189
1190 """
1191 if not self.revisionExpr.startswith(R_TAGS):
1192 return self.GetRevisionId(self._allrefs)
1193
1194 try:
1195 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1196 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001197 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1198 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001199
David Pursehouse8a68ff92012-09-24 12:15:13 +09001200 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001201 if self.revisionId:
1202 return self.revisionId
1203
1204 rem = self.GetRemote(self.remote.name)
1205 rev = rem.ToLocal(self.revisionExpr)
1206
David Pursehouse8a68ff92012-09-24 12:15:13 +09001207 if all_refs is not None and rev in all_refs:
1208 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001209
1210 try:
1211 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1212 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001213 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1214 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001215
Raman Tenneti6a872c92021-01-14 19:17:50 -08001216 def SetRevisionId(self, revisionId):
1217 self.revisionId = revisionId
1218
Martin Kellye4e94d22017-03-21 16:05:12 -07001219 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001220 """Perform only the local IO portion of the sync process.
1221 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001222 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001223 if not os.path.exists(self.gitdir):
1224 syncbuf.fail(self,
1225 'Cannot checkout %s due to missing network sync; Run '
1226 '`repo sync -n %s` first.' %
1227 (self.name, self.name))
1228 return
1229
Martin Kellye4e94d22017-03-21 16:05:12 -07001230 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001231 all_refs = self.bare_ref.all
1232 self.CleanPublishedCache(all_refs)
1233 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001234
Mike Frysinger0458faa2021-03-10 23:35:44 -05001235 # Special case the root of the repo client checkout. Make sure it doesn't
1236 # contain files being checked out to dirs we don't allow.
1237 if self.relpath == '.':
1238 PROTECTED_PATHS = {'.repo'}
1239 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1240 bad_paths = paths & PROTECTED_PATHS
1241 if bad_paths:
1242 syncbuf.fail(self,
1243 'Refusing to checkout project that writes to protected '
1244 'paths: %s' % (', '.join(bad_paths),))
1245 return
1246
David Pursehouse1d947b32012-10-25 12:23:11 +09001247 def _doff():
1248 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001249 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001250
Martin Kellye4e94d22017-03-21 16:05:12 -07001251 def _dosubmodules():
1252 self._SyncSubmodules(quiet=True)
1253
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001254 head = self.work_git.GetHead()
1255 if head.startswith(R_HEADS):
1256 branch = head[len(R_HEADS):]
1257 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001258 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001259 except KeyError:
1260 head = None
1261 else:
1262 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001263
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001264 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001265 # Currently on a detached HEAD. The user is assumed to
1266 # not have any local modifications worth worrying about.
1267 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001268 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001269 syncbuf.fail(self, _PriorSyncFailedError())
1270 return
1271
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001272 if head == revid:
1273 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001274 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001275 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001276 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001277 # The copy/linkfile config may have changed.
1278 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001279 return
1280 else:
1281 lost = self._revlist(not_rev(revid), HEAD)
1282 if lost:
1283 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001284
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001285 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001286 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001287 if submodules:
1288 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001289 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001290 syncbuf.fail(self, e)
1291 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001292 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001293 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001294
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001295 if head == revid:
1296 # No changes; don't do anything further.
1297 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001298 # The copy/linkfile config may have changed.
1299 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001300 return
1301
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001302 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001303
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001304 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001305 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001306 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001307 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001308 syncbuf.info(self,
1309 "leaving %s; does not track upstream",
1310 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001311 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001312 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001313 if submodules:
1314 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001315 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001316 syncbuf.fail(self, e)
1317 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001318 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001319 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001320
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001321 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001322
1323 # See if we can perform a fast forward merge. This can happen if our
1324 # branch isn't in the exact same state as we last published.
1325 try:
1326 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1327 # Skip the published logic.
1328 pub = False
1329 except GitError:
1330 pub = self.WasPublished(branch.name, all_refs)
1331
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001332 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001333 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001334 if not_merged:
1335 if upstream_gain:
1336 # The user has published this branch and some of those
1337 # commits are not yet merged upstream. We do not want
1338 # to rewrite the published commits so we punt.
1339 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001340 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001341 "branch %s is published (but not merged) and is now "
1342 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001343 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001344 elif pub == head:
1345 # All published commits are merged, and thus we are a
1346 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001347 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001348 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001349 if submodules:
1350 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001351 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001352
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001353 # Examine the local commits not in the remote. Find the
1354 # last one attributed to this user, if any.
1355 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001356 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001357 last_mine = None
1358 cnt_mine = 0
1359 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001360 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001361 if committer_email == self.UserEmail:
1362 last_mine = commit_id
1363 cnt_mine += 1
1364
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001365 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001366 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001367
1368 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001369 syncbuf.fail(self, _DirtyError())
1370 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001371
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001372 # If the upstream switched on us, warn the user.
1373 #
1374 if branch.merge != self.revisionExpr:
1375 if branch.merge and self.revisionExpr:
1376 syncbuf.info(self,
1377 'manifest switched %s...%s',
1378 branch.merge,
1379 self.revisionExpr)
1380 elif branch.merge:
1381 syncbuf.info(self,
1382 'manifest no longer tracks %s',
1383 branch.merge)
1384
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001385 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001386 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001387 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001388 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001389 syncbuf.info(self,
1390 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001391 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001392
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001393 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001394 if not ID_RE.match(self.revisionExpr):
1395 # in case of manifest sync the revisionExpr might be a SHA1
1396 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001397 if not branch.merge.startswith('refs/'):
1398 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001399 branch.Save()
1400
Mike Pontillod3153822012-02-28 11:53:24 -08001401 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001402 def _docopyandlink():
1403 self._CopyAndLinkFiles()
1404
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001405 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001406 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001407 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001408 if submodules:
1409 syncbuf.later2(self, _dosubmodules)
1410 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001411 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001412 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001413 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001414 if submodules:
1415 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001416 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001417 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001418 syncbuf.fail(self, e)
1419 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001420 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001421 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001422 if submodules:
1423 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001424
Mike Frysingere6a202f2019-08-02 15:57:57 -04001425 def AddCopyFile(self, src, dest, topdir):
1426 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001427
Mike Frysingere6a202f2019-08-02 15:57:57 -04001428 No filesystem changes occur here. Actual copying happens later on.
1429
1430 Paths should have basic validation run on them before being queued.
1431 Further checking will be handled when the actual copy happens.
1432 """
1433 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1434
1435 def AddLinkFile(self, src, dest, topdir):
1436 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1437
1438 No filesystem changes occur here. Actual linking happens later on.
1439
1440 Paths should have basic validation run on them before being queued.
1441 Further checking will be handled when the actual link happens.
1442 """
1443 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001444
James W. Mills24c13082012-04-12 15:04:13 -05001445 def AddAnnotation(self, name, value, keep):
1446 self.annotations.append(_Annotation(name, value, keep))
1447
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001448 def DownloadPatchSet(self, change_id, patch_id):
1449 """Download a single patch set of a single change to FETCH_HEAD.
1450 """
1451 remote = self.GetRemote(self.remote.name)
1452
1453 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001454 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001455 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001456 if GitCommand(self, cmd, bare=True).Wait() != 0:
1457 return None
1458 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001459 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001460 change_id,
1461 patch_id,
1462 self.bare_git.rev_parse('FETCH_HEAD'))
1463
Mike Frysingerc0d18662020-02-19 19:19:18 -05001464 def DeleteWorktree(self, quiet=False, force=False):
1465 """Delete the source checkout and any other housekeeping tasks.
1466
1467 This currently leaves behind the internal .repo/ cache state. This helps
1468 when switching branches or manifest changes get reverted as we don't have
1469 to redownload all the git objects. But we should do some GC at some point.
1470
1471 Args:
1472 quiet: Whether to hide normal messages.
1473 force: Always delete tree even if dirty.
1474
1475 Returns:
1476 True if the worktree was completely cleaned out.
1477 """
1478 if self.IsDirty():
1479 if force:
1480 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1481 (self.relpath,), file=sys.stderr)
1482 else:
1483 print('error: %s: Cannot remove project: uncommitted changes are '
1484 'present.\n' % (self.relpath,), file=sys.stderr)
1485 return False
1486
1487 if not quiet:
1488 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1489
1490 # Unlock and delink from the main worktree. We don't use git's worktree
1491 # remove because it will recursively delete projects -- we handle that
1492 # ourselves below. https://crbug.com/git/48
1493 if self.use_git_worktrees:
1494 needle = platform_utils.realpath(self.gitdir)
1495 # Find the git worktree commondir under .repo/worktrees/.
1496 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1497 assert output.startswith('worktree '), output
1498 commondir = output[9:]
1499 # Walk each of the git worktrees to see where they point.
1500 configs = os.path.join(commondir, 'worktrees')
1501 for name in os.listdir(configs):
1502 gitdir = os.path.join(configs, name, 'gitdir')
1503 with open(gitdir) as fp:
1504 relpath = fp.read().strip()
1505 # Resolve the checkout path and see if it matches this project.
1506 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1507 if fullpath == needle:
1508 platform_utils.rmtree(os.path.join(configs, name))
1509
1510 # Delete the .git directory first, so we're less likely to have a partially
1511 # working git repository around. There shouldn't be any git projects here,
1512 # so rmtree works.
1513
1514 # Try to remove plain files first in case of git worktrees. If this fails
1515 # for any reason, we'll fall back to rmtree, and that'll display errors if
1516 # it can't remove things either.
1517 try:
1518 platform_utils.remove(self.gitdir)
1519 except OSError:
1520 pass
1521 try:
1522 platform_utils.rmtree(self.gitdir)
1523 except OSError as e:
1524 if e.errno != errno.ENOENT:
1525 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1526 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1527 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1528 return False
1529
1530 # Delete everything under the worktree, except for directories that contain
1531 # another git project.
1532 dirs_to_remove = []
1533 failed = False
1534 for root, dirs, files in platform_utils.walk(self.worktree):
1535 for f in files:
1536 path = os.path.join(root, f)
1537 try:
1538 platform_utils.remove(path)
1539 except OSError as e:
1540 if e.errno != errno.ENOENT:
1541 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1542 failed = True
1543 dirs[:] = [d for d in dirs
1544 if not os.path.lexists(os.path.join(root, d, '.git'))]
1545 dirs_to_remove += [os.path.join(root, d) for d in dirs
1546 if os.path.join(root, d) not in dirs_to_remove]
1547 for d in reversed(dirs_to_remove):
1548 if platform_utils.islink(d):
1549 try:
1550 platform_utils.remove(d)
1551 except OSError as e:
1552 if e.errno != errno.ENOENT:
1553 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1554 failed = True
1555 elif not platform_utils.listdir(d):
1556 try:
1557 platform_utils.rmdir(d)
1558 except OSError as e:
1559 if e.errno != errno.ENOENT:
1560 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1561 failed = True
1562 if failed:
1563 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1564 file=sys.stderr)
1565 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1566 return False
1567
1568 # Try deleting parent dirs if they are empty.
1569 path = self.worktree
1570 while path != self.manifest.topdir:
1571 try:
1572 platform_utils.rmdir(path)
1573 except OSError as e:
1574 if e.errno != errno.ENOENT:
1575 break
1576 path = os.path.dirname(path)
1577
1578 return True
1579
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001580# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001581 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582 """Create a new branch off the manifest's revision.
1583 """
Simran Basib9a1b732015-08-20 12:19:28 -07001584 if not branch_merge:
1585 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001586 head = self.work_git.GetHead()
1587 if head == (R_HEADS + name):
1588 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001589
David Pursehouse8a68ff92012-09-24 12:15:13 +09001590 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001591 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001592 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001593 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001594 capture_stdout=True,
1595 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001596
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001597 branch = self.GetBranch(name)
1598 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001599 branch.merge = branch_merge
1600 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1601 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001602
1603 if revision is None:
1604 revid = self.GetRevisionId(all_refs)
1605 else:
1606 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001607
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001608 if head.startswith(R_HEADS):
1609 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001610 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001611 except KeyError:
1612 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001613 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001614 ref = R_HEADS + name
1615 self.work_git.update_ref(ref, revid)
1616 self.work_git.symbolic_ref(HEAD, ref)
1617 branch.Save()
1618 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001619
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001620 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001621 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001622 capture_stdout=True,
1623 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001624 branch.Save()
1625 return True
1626 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627
Wink Saville02d79452009-04-10 13:01:24 -07001628 def CheckoutBranch(self, name):
1629 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001630
1631 Args:
1632 name: The name of the branch to checkout.
1633
1634 Returns:
1635 True if the checkout succeeded; False if it didn't; None if the branch
1636 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001637 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001638 rev = R_HEADS + name
1639 head = self.work_git.GetHead()
1640 if head == rev:
1641 # Already on the branch
1642 #
1643 return True
Wink Saville02d79452009-04-10 13:01:24 -07001644
David Pursehouse8a68ff92012-09-24 12:15:13 +09001645 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001646 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001647 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001648 except KeyError:
1649 # Branch does not exist in this project
1650 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001651 return None
Wink Saville02d79452009-04-10 13:01:24 -07001652
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001653 if head.startswith(R_HEADS):
1654 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001655 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001656 except KeyError:
1657 head = None
1658
1659 if head == revid:
1660 # Same revision; just update HEAD to point to the new
1661 # target branch, but otherwise take no other action.
1662 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001663 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1664 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001665 return True
1666
1667 return GitCommand(self,
1668 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001669 capture_stdout=True,
1670 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001671
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001672 def AbandonBranch(self, name):
1673 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001674
1675 Args:
1676 name: The name of the branch to abandon.
1677
1678 Returns:
1679 True if the abandon succeeded; False if it didn't; None if the branch
1680 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001681 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001682 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001683 all_refs = self.bare_ref.all
1684 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001685 # Doesn't exist
1686 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001687
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001688 head = self.work_git.GetHead()
1689 if head == rev:
1690 # We can't destroy the branch while we are sitting
1691 # on it. Switch to a detached HEAD.
1692 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001693 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001694
David Pursehouse8a68ff92012-09-24 12:15:13 +09001695 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001696 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001697 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001698 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001699 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001700
1701 return GitCommand(self,
1702 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001703 capture_stdout=True,
1704 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001705
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001706 def PruneHeads(self):
1707 """Prune any topic branches already merged into upstream.
1708 """
1709 cb = self.CurrentBranch
1710 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001711 left = self._allrefs
1712 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001713 if name.startswith(R_HEADS):
1714 name = name[len(R_HEADS):]
1715 if cb is None or name != cb:
1716 kill.append(name)
1717
Mike Frysingera3794e92021-03-11 23:24:01 -05001718 # Minor optimization: If there's nothing to prune, then don't try to read
1719 # any project state.
1720 if not kill and not cb:
1721 return []
1722
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001723 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001724 if cb is not None \
1725 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001726 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727 self.work_git.DetachHead(HEAD)
1728 kill.append(cb)
1729
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001730 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001731 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001733 try:
1734 self.bare_git.DetachHead(rev)
1735
1736 b = ['branch', '-d']
1737 b.extend(kill)
1738 b = GitCommand(self, b, bare=True,
1739 capture_stdout=True,
1740 capture_stderr=True)
1741 b.Wait()
1742 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001743 if ID_RE.match(old):
1744 self.bare_git.DetachHead(old)
1745 else:
1746 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001747 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001748
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001749 for branch in kill:
1750 if (R_HEADS + branch) not in left:
1751 self.CleanPublishedCache()
1752 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001753
1754 if cb and cb not in kill:
1755 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001756 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001757
1758 kept = []
1759 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001760 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001761 branch = self.GetBranch(branch)
1762 base = branch.LocalMerge
1763 if not base:
1764 base = rev
1765 kept.append(ReviewableBranch(self, branch, base))
1766 return kept
1767
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001768# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001769 def GetRegisteredSubprojects(self):
1770 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001771
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001772 def rec(subprojects):
1773 if not subprojects:
1774 return
1775 result.extend(subprojects)
1776 for p in subprojects:
1777 rec(p.subprojects)
1778 rec(self.subprojects)
1779 return result
1780
1781 def _GetSubmodules(self):
1782 # Unfortunately we cannot call `git submodule status --recursive` here
1783 # because the working tree might not exist yet, and it cannot be used
1784 # without a working tree in its current implementation.
1785
1786 def get_submodules(gitdir, rev):
1787 # Parse .gitmodules for submodule sub_paths and sub_urls
1788 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1789 if not sub_paths:
1790 return []
1791 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1792 # revision of submodule repository
1793 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1794 submodules = []
1795 for sub_path, sub_url in zip(sub_paths, sub_urls):
1796 try:
1797 sub_rev = sub_revs[sub_path]
1798 except KeyError:
1799 # Ignore non-exist submodules
1800 continue
1801 submodules.append((sub_rev, sub_path, sub_url))
1802 return submodules
1803
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001804 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1805 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001806
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001807 def parse_gitmodules(gitdir, rev):
1808 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1809 try:
Anthony King7bdac712014-07-16 12:56:40 +01001810 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1811 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001812 except GitError:
1813 return [], []
1814 if p.Wait() != 0:
1815 return [], []
1816
1817 gitmodules_lines = []
1818 fd, temp_gitmodules_path = tempfile.mkstemp()
1819 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001820 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001821 os.close(fd)
1822 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001823 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1824 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001825 if p.Wait() != 0:
1826 return [], []
1827 gitmodules_lines = p.stdout.split('\n')
1828 except GitError:
1829 return [], []
1830 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001831 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001832
1833 names = set()
1834 paths = {}
1835 urls = {}
1836 for line in gitmodules_lines:
1837 if not line:
1838 continue
1839 m = re_path.match(line)
1840 if m:
1841 names.add(m.group(1))
1842 paths[m.group(1)] = m.group(2)
1843 continue
1844 m = re_url.match(line)
1845 if m:
1846 names.add(m.group(1))
1847 urls[m.group(1)] = m.group(2)
1848 continue
1849 names = sorted(names)
1850 return ([paths.get(name, '') for name in names],
1851 [urls.get(name, '') for name in names])
1852
1853 def git_ls_tree(gitdir, rev, paths):
1854 cmd = ['ls-tree', rev, '--']
1855 cmd.extend(paths)
1856 try:
Anthony King7bdac712014-07-16 12:56:40 +01001857 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1858 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001859 except GitError:
1860 return []
1861 if p.Wait() != 0:
1862 return []
1863 objects = {}
1864 for line in p.stdout.split('\n'):
1865 if not line.strip():
1866 continue
1867 object_rev, object_path = line.split()[2:4]
1868 objects[object_path] = object_rev
1869 return objects
1870
1871 try:
1872 rev = self.GetRevisionId()
1873 except GitError:
1874 return []
1875 return get_submodules(self.gitdir, rev)
1876
1877 def GetDerivedSubprojects(self):
1878 result = []
1879 if not self.Exists:
1880 # If git repo does not exist yet, querying its submodules will
1881 # mess up its states; so return here.
1882 return result
1883 for rev, path, url in self._GetSubmodules():
1884 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001885 relpath, worktree, gitdir, objdir = \
1886 self.manifest.GetSubprojectPaths(self, name, path)
1887 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001888 if project:
1889 result.extend(project.GetDerivedSubprojects())
1890 continue
David James8d201162013-10-11 17:03:19 -07001891
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001892 if url.startswith('..'):
1893 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001894 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001895 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001896 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001897 review=self.remote.review,
1898 revision=self.remote.revision)
1899 subproject = Project(manifest=self.manifest,
1900 name=name,
1901 remote=remote,
1902 gitdir=gitdir,
1903 objdir=objdir,
1904 worktree=worktree,
1905 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001906 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001907 revisionId=rev,
1908 rebase=self.rebase,
1909 groups=self.groups,
1910 sync_c=self.sync_c,
1911 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001912 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001913 parent=self,
1914 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001915 result.append(subproject)
1916 result.extend(subproject.GetDerivedSubprojects())
1917 return result
1918
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001919# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001920 def EnableRepositoryExtension(self, key, value='true', version=1):
1921 """Enable git repository extension |key| with |value|.
1922
1923 Args:
1924 key: The extension to enabled. Omit the "extensions." prefix.
1925 value: The value to use for the extension.
1926 version: The minimum git repository version needed.
1927 """
1928 # Make sure the git repo version is new enough already.
1929 found_version = self.config.GetInt('core.repositoryFormatVersion')
1930 if found_version is None:
1931 found_version = 0
1932 if found_version < version:
1933 self.config.SetString('core.repositoryFormatVersion', str(version))
1934
1935 # Enable the extension!
1936 self.config.SetString('extensions.%s' % (key,), value)
1937
Mike Frysinger50a81de2020-09-06 15:51:21 -04001938 def ResolveRemoteHead(self, name=None):
1939 """Find out what the default branch (HEAD) points to.
1940
1941 Normally this points to refs/heads/master, but projects are moving to main.
1942 Support whatever the server uses rather than hardcoding "master" ourselves.
1943 """
1944 if name is None:
1945 name = self.remote.name
1946
1947 # The output will look like (NB: tabs are separators):
1948 # ref: refs/heads/master HEAD
1949 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1950 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1951
1952 for line in output.splitlines():
1953 lhs, rhs = line.split('\t', 1)
1954 if rhs == 'HEAD' and lhs.startswith('ref:'):
1955 return lhs[4:].strip()
1956
1957 return None
1958
Zac Livingstone4332262017-06-16 08:56:09 -06001959 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001960 try:
1961 # if revision (sha or tag) is not present then following function
1962 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001963 self.bare_git.rev_list('-1', '--missing=allow-any',
1964 '%s^0' % self.revisionExpr, '--')
Chris AtLee2fb64662014-01-16 21:32:33 -05001965 return True
1966 except GitError:
1967 # There is no such persistent revision. We have to fetch it.
1968 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001969
Julien Campergue335f5ef2013-10-16 11:02:35 +02001970 def _FetchArchive(self, tarpath, cwd=None):
1971 cmd = ['archive', '-v', '-o', tarpath]
1972 cmd.append('--remote=%s' % self.remote.url)
1973 cmd.append('--prefix=%s/' % self.relpath)
1974 cmd.append(self.revisionExpr)
1975
1976 command = GitCommand(self, cmd, cwd=cwd,
1977 capture_stdout=True,
1978 capture_stderr=True)
1979
1980 if command.Wait() != 0:
1981 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1982
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001983 def _RemoteFetch(self, name=None,
1984 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001985 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001986 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001987 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001988 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001989 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001990 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001991 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001992 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001993 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001994 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001995 clone_filter=None,
1996 retry_fetches=2,
1997 retry_sleep_initial_sec=4.0,
1998 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001999 is_sha1 = False
2000 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002001 # The depth should not be used when fetching to a mirror because
2002 # it will result in a shallow repository that cannot be cloned or
2003 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002004 # The repo project should also never be synced with partial depth.
2005 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2006 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002007
Shawn Pearce69e04d82014-01-29 12:48:54 -08002008 if depth:
2009 current_branch_only = True
2010
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002011 if ID_RE.match(self.revisionExpr) is not None:
2012 is_sha1 = True
2013
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002014 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002015 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002016 # this is a tag and its sha1 value should never change
2017 tag_name = self.revisionExpr[len(R_TAGS):]
2018
2019 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002020 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002021 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002022 print('Skipped fetching project %s (already have persistent ref)'
2023 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002024 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002025 if is_sha1 and not depth:
2026 # When syncing a specific commit and --depth is not set:
2027 # * if upstream is explicitly specified and is not a sha1, fetch only
2028 # upstream as users expect only upstream to be fetch.
2029 # Note: The commit might not be in upstream in which case the sync
2030 # will fail.
2031 # * otherwise, fetch all branches to make sure we end up with the
2032 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002033 if self.upstream:
2034 current_branch_only = not ID_RE.match(self.upstream)
2035 else:
2036 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002037
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002038 if not name:
2039 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002040
2041 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002042 remote = self.GetRemote(name)
2043 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002044 ssh_proxy = True
2045
Shawn O. Pearce88443382010-10-08 10:02:09 +02002046 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002047 if alt_dir and 'objects' == os.path.basename(alt_dir):
2048 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002049 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2050 remote = self.GetRemote(name)
2051
David Pursehouse8a68ff92012-09-24 12:15:13 +09002052 all_refs = self.bare_ref.all
2053 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002054 tmp = set()
2055
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302056 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002057 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002058 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002059 all_refs[r] = ref_id
2060 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002061 continue
2062
David Pursehouse8a68ff92012-09-24 12:15:13 +09002063 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002064 continue
2065
David Pursehouse8a68ff92012-09-24 12:15:13 +09002066 r = 'refs/_alt/%s' % ref_id
2067 all_refs[r] = ref_id
2068 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002069 tmp.add(r)
2070
heping3d7bbc92017-04-12 19:51:47 +08002071 tmp_packed_lines = []
2072 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002073
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302074 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002075 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002076 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002077 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002078 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002079
heping3d7bbc92017-04-12 19:51:47 +08002080 tmp_packed = ''.join(tmp_packed_lines)
2081 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002082 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002083 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002084 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002085
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002086 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002087
Xin Li745be2e2019-06-03 11:24:30 -07002088 if clone_filter:
2089 git_require((2, 19, 0), fail=True, msg='partial clones')
2090 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002091 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002092
Conley Owensf97e8382015-01-21 11:12:46 -08002093 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002094 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002095 else:
2096 # If this repo has shallow objects, then we don't know which refs have
2097 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2098 # do this with projects that don't have shallow objects, since it is less
2099 # efficient.
2100 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2101 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002102
Mike Frysinger4847e052020-02-22 00:07:35 -05002103 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002104 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002105 if not quiet and sys.stdout.isatty():
2106 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002107 if not self.worktree:
2108 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002109 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002110
Mike Frysingere57f1142019-03-18 21:27:54 -04002111 if force_sync:
2112 cmd.append('--force')
2113
David Pursehouse74cfd272015-10-14 10:50:15 +09002114 if prune:
2115 cmd.append('--prune')
2116
Martin Kellye4e94d22017-03-21 16:05:12 -07002117 if submodules:
2118 cmd.append('--recurse-submodules=on-demand')
2119
Kuang-che Wu6856f982019-11-25 12:37:55 +08002120 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002121 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002122 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002123 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002124 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002125 spec.append('tag')
2126 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002127
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302128 if self.manifest.IsMirror and not current_branch_only:
2129 branch = None
2130 else:
2131 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002132 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002133 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002134 # Shallow checkout of a specific commit, fetch from that commit and not
2135 # the heads only as the commit might be deeper in the history.
2136 spec.append(branch)
2137 else:
2138 if is_sha1:
2139 branch = self.upstream
2140 if branch is not None and branch.strip():
2141 if not branch.startswith('refs/'):
2142 branch = R_HEADS + branch
2143 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2144
2145 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2146 # whole repo.
2147 if self.manifest.IsMirror and not spec:
2148 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2149
2150 # If using depth then we should not get all the tags since they may
2151 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002152 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002153 cmd.append('--no-tags')
2154 else:
2155 cmd.append('--tags')
2156 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2157
Conley Owens80b87fe2014-05-09 17:13:44 -07002158 cmd.extend(spec)
2159
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002160 # At least one retry minimum due to git remote prune.
2161 retry_fetches = max(retry_fetches, 2)
2162 retry_cur_sleep = retry_sleep_initial_sec
2163 ok = prune_tried = False
2164 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002165 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002166 merge_output=True, capture_stdout=quiet or bool(output_redir))
2167 if gitcmd.stdout and not quiet and output_redir:
2168 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002169 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002170 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002171 ok = True
2172 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002173
2174 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002175 elif (gitcmd.stdout and
2176 'error:' in gitcmd.stdout and
2177 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002178 # Fallthru to sleep+retry logic at the bottom.
2179 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002180
Mike Frysinger6823bc22021-04-15 02:06:28 -04002181 # Try to prune remote branches once in case there are conflicts.
2182 # For example, if the remote had refs/heads/upstream, but deleted that and
2183 # now has refs/heads/upstream/foo.
2184 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002185 'error:' in gitcmd.stdout and
2186 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002187 not prune_tried):
2188 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002189 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002190 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002191 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002192 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002193 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002194 output_redir.write('retrying fetch after pruning remote branches')
2195 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002196 continue
Brian Harring14a66742012-09-28 20:21:57 -07002197 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002198 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2199 # in sha1 mode, we just tried sync'ing from the upstream field; it
2200 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002201 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002202 elif ret < 0:
2203 # Git died with a signal, exit immediately
2204 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002205
2206 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysinger31990f02020-02-17 01:35:18 -05002207 if not verbose:
Mike Frysinger6823bc22021-04-15 02:06:28 -04002208 output_redir.write('\n%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
2209 if try_n < retry_fetches - 1:
2210 output_redir.write('sleeping %s seconds before retrying' % retry_cur_sleep)
2211 time.sleep(retry_cur_sleep)
2212 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2213 MAXIMUM_RETRY_SLEEP_SEC)
2214 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2215 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002216
2217 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002218 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002219 if old_packed != '':
2220 _lwrite(packed_refs, old_packed)
2221 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002222 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002223 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002224
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002225 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002226 # We just synced the upstream given branch; verify we
2227 # got what we wanted, else trigger a second run of all
2228 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002229 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002230 # Sync the current branch only with depth set to None.
2231 # We always pass depth=None down to avoid infinite recursion.
2232 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002233 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002234 current_branch_only=current_branch_only and depth,
2235 initial=False, alt_dir=alt_dir,
2236 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002237
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002238 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002239
Mike Frysingere50b6a72020-02-19 01:45:48 -05002240 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002241 if initial and \
2242 (self.manifest.manifestProject.config.GetString('repo.depth') or
2243 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002244 return False
2245
2246 remote = self.GetRemote(self.remote.name)
2247 bundle_url = remote.url + '/clone.bundle'
2248 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002249 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2250 'persistent-http',
2251 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002252 return False
2253
2254 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2255 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2256
2257 exist_dst = os.path.exists(bundle_dst)
2258 exist_tmp = os.path.exists(bundle_tmp)
2259
2260 if not initial and not exist_dst and not exist_tmp:
2261 return False
2262
2263 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002264 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2265 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002266 if not exist_dst:
2267 return False
2268
2269 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002270 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002271 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002272 if not quiet and sys.stdout.isatty():
2273 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002274 if not self.worktree:
2275 cmd.append('--update-head-ok')
2276 cmd.append(bundle_dst)
2277 for f in remote.fetch:
2278 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002279 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002280
2281 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002282 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002283 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002284 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002285 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002286 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002287
Mike Frysingere50b6a72020-02-19 01:45:48 -05002288 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002289 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002290 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002291
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002292 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002293 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002294 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002295 if os.path.exists(tmpPath):
2296 size = os.stat(tmpPath).st_size
2297 if size >= 1024:
2298 cmd += ['--continue-at', '%d' % (size,)]
2299 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002300 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002301 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002302 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002303 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002304 if proxy:
2305 cmd += ['--proxy', proxy]
2306 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2307 cmd += ['--proxy', os.environ['http_proxy']]
2308 if srcUrl.startswith('persistent-https'):
2309 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2310 elif srcUrl.startswith('persistent-http'):
2311 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002312 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002313
Dave Borowitz137d0132015-01-02 11:12:54 -08002314 if IsTrace():
2315 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002316 if verbose:
2317 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2318 stdout = None if verbose else subprocess.PIPE
2319 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002320 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002321 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002322 except OSError:
2323 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002324
Mike Frysingere50b6a72020-02-19 01:45:48 -05002325 (output, _) = proc.communicate()
2326 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002327
Dave Borowitz137d0132015-01-02 11:12:54 -08002328 if curlret == 22:
2329 # From curl man page:
2330 # 22: HTTP page not retrieved. The requested url was not found or
2331 # returned another error with the HTTP error code being 400 or above.
2332 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002333 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002334 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2335 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002336 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002337 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002338 elif curlret and not verbose and output:
2339 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002340
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002341 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002342 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002343 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002344 return True
2345 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002346 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002347 return False
2348 else:
2349 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002350
Kris Giesingc8d882a2014-12-23 13:02:32 -08002351 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002352 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002353 with open(path, 'rb') as f:
2354 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002355 return True
2356 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002357 if not quiet:
2358 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002359 return False
2360 except OSError:
2361 return False
2362
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002363 def _Checkout(self, rev, quiet=False):
2364 cmd = ['checkout']
2365 if quiet:
2366 cmd.append('-q')
2367 cmd.append(rev)
2368 cmd.append('--')
2369 if GitCommand(self, cmd).Wait() != 0:
2370 if self._allrefs:
2371 raise GitError('%s checkout %s ' % (self.name, rev))
2372
Mike Frysinger915fda12020-03-22 12:15:20 -04002373 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002374 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002375 if ffonly:
2376 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002377 if record_origin:
2378 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002379 cmd.append(rev)
2380 cmd.append('--')
2381 if GitCommand(self, cmd).Wait() != 0:
2382 if self._allrefs:
2383 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2384
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302385 def _LsRemote(self, refs):
2386 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302387 p = GitCommand(self, cmd, capture_stdout=True)
2388 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002389 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302390 return None
2391
Anthony King7bdac712014-07-16 12:56:40 +01002392 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002393 cmd = ['revert']
2394 cmd.append('--no-edit')
2395 cmd.append(rev)
2396 cmd.append('--')
2397 if GitCommand(self, cmd).Wait() != 0:
2398 if self._allrefs:
2399 raise GitError('%s revert %s ' % (self.name, rev))
2400
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002401 def _ResetHard(self, rev, quiet=True):
2402 cmd = ['reset', '--hard']
2403 if quiet:
2404 cmd.append('-q')
2405 cmd.append(rev)
2406 if GitCommand(self, cmd).Wait() != 0:
2407 raise GitError('%s reset --hard %s ' % (self.name, rev))
2408
Martin Kellye4e94d22017-03-21 16:05:12 -07002409 def _SyncSubmodules(self, quiet=True):
2410 cmd = ['submodule', 'update', '--init', '--recursive']
2411 if quiet:
2412 cmd.append('-q')
2413 if GitCommand(self, cmd).Wait() != 0:
2414 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2415
Anthony King7bdac712014-07-16 12:56:40 +01002416 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002417 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002418 if onto is not None:
2419 cmd.extend(['--onto', onto])
2420 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002421 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002422 raise GitError('%s rebase %s ' % (self.name, upstream))
2423
Pierre Tardy3d125942012-05-04 12:18:12 +02002424 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002425 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002426 if ffonly:
2427 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002428 if GitCommand(self, cmd).Wait() != 0:
2429 raise GitError('%s merge %s ' % (self.name, head))
2430
David Pursehousee8ace262020-02-13 12:41:15 +09002431 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002432 init_git_dir = not os.path.exists(self.gitdir)
2433 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002434 try:
2435 # Initialize the bare repository, which contains all of the objects.
2436 if init_obj_dir:
2437 os.makedirs(self.objdir)
2438 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002439
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002440 if self.use_git_worktrees:
2441 # Set up the m/ space to point to the worktree-specific ref space.
2442 # We'll update the worktree-specific ref space on each checkout.
2443 if self.manifest.branch:
2444 self.bare_git.symbolic_ref(
2445 '-m', 'redirecting to worktree scope',
2446 R_M + self.manifest.branch,
2447 R_WORKTREE_M + self.manifest.branch)
2448
2449 # Enable per-worktree config file support if possible. This is more a
2450 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002451 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002452 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002453
Kevin Degib1a07b82015-07-27 13:33:43 -06002454 # If we have a separate directory to hold refs, initialize it as well.
2455 if self.objdir != self.gitdir:
2456 if init_git_dir:
2457 os.makedirs(self.gitdir)
2458
2459 if init_obj_dir or init_git_dir:
2460 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2461 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002462 try:
2463 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2464 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002465 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002466 print("Retrying clone after deleting %s" %
2467 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002468 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002469 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2470 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002471 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002472 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002473 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2474 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002475 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002476 raise e
2477 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002478
Kevin Degi384b3c52014-10-16 16:02:58 -06002479 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002480 mp = self.manifest.manifestProject
2481 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002482
Kevin Degib1a07b82015-07-27 13:33:43 -06002483 if ref_dir or mirror_git:
2484 if not mirror_git:
2485 mirror_git = os.path.join(ref_dir, self.name + '.git')
2486 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2487 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002488 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2489 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002490
Kevin Degib1a07b82015-07-27 13:33:43 -06002491 if os.path.exists(mirror_git):
2492 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002493 elif os.path.exists(repo_git):
2494 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002495 elif os.path.exists(worktrees_git):
2496 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002497 else:
2498 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002499
Kevin Degib1a07b82015-07-27 13:33:43 -06002500 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002501 if not os.path.isabs(ref_dir):
2502 # The alternate directory is relative to the object database.
2503 ref_dir = os.path.relpath(ref_dir,
2504 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002505 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2506 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002507
David Pursehousee8ace262020-02-13 12:41:15 +09002508 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002509
2510 m = self.manifest.manifestProject.config
2511 for key in ['user.name', 'user.email']:
2512 if m.Has(key, include_defaults=False):
2513 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002514 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002515 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002516 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002517 except Exception:
2518 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002519 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002520 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002521 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002522 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002523
David Pursehousee8ace262020-02-13 12:41:15 +09002524 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002525 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002526 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002527
David Pursehousee8ace262020-02-13 12:41:15 +09002528 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002529 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002530 if not os.path.exists(hooks):
2531 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002532 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002533 name = os.path.basename(stock_hook)
2534
Victor Boivie65e0f352011-04-18 11:23:29 +02002535 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002536 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002537 # Don't install a Gerrit Code Review hook if this
2538 # project does not appear to use it for reviews.
2539 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002540 # Since the manifest project is one of those, but also
2541 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002542 continue
2543
2544 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002545 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002546 continue
2547 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002548 # If the files are the same, we'll leave it alone. We create symlinks
2549 # below by default but fallback to hardlinks if the OS blocks them.
2550 # So if we're here, it's probably because we made a hardlink below.
2551 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002552 if not quiet:
2553 _warn("%s: Not replacing locally modified %s hook",
2554 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002555 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002556 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002557 platform_utils.symlink(
2558 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002559 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002560 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002561 try:
2562 os.link(stock_hook, dst)
2563 except OSError:
2564 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002565 else:
2566 raise
2567
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002568 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002569 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002570 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002571 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002572 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002573 remote.review = self.remote.review
2574 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002575
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002576 if self.worktree:
2577 remote.ResetFetch(mirror=False)
2578 else:
2579 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002580 remote.Save()
2581
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002582 def _InitMRef(self):
2583 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002584 if self.use_git_worktrees:
2585 # We can't update this ref with git worktrees until it exists.
2586 # We'll wait until the initial checkout to set it.
2587 if not os.path.exists(self.worktree):
2588 return
2589
2590 base = R_WORKTREE_M
2591 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002592
2593 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002594 else:
2595 base = R_M
2596 active_git = self.bare_git
2597
2598 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002599
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002600 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002601 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002602
Remy Böhmer1469c282020-12-15 18:49:02 +01002603 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002604 cur = self.bare_ref.symref(ref)
2605
2606 if self.revisionId:
2607 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2608 msg = 'manifest set to %s' % self.revisionId
2609 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002610 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002611 else:
2612 remote = self.GetRemote(self.remote.name)
2613 dst = remote.ToLocal(self.revisionExpr)
2614 if cur != dst:
2615 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002616 if detach:
2617 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2618 else:
2619 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002620
Kevin Degi384b3c52014-10-16 16:02:58 -06002621 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002622 # Git worktrees don't use symlinks to share at all.
2623 if self.use_git_worktrees:
2624 return
2625
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002626 symlink_files = self.shareable_files[:]
2627 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002628 if share_refs:
2629 symlink_files += self.working_tree_files
2630 symlink_dirs += self.working_tree_dirs
2631 to_symlink = symlink_files + symlink_dirs
2632 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002633 # Try to self-heal a bit in simple cases.
2634 dst_path = os.path.join(destdir, name)
2635 src_path = os.path.join(srcdir, name)
2636
2637 if name in self.working_tree_dirs:
2638 # If the dir is missing under .repo/projects/, create it.
2639 if not os.path.exists(src_path):
2640 os.makedirs(src_path)
2641
2642 elif name in self.working_tree_files:
2643 # If it's a file under the checkout .git/ and the .repo/projects/ has
2644 # nothing, move the file under the .repo/projects/ tree.
2645 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2646 platform_utils.rename(dst_path, src_path)
2647
2648 # If the path exists under the .repo/projects/ and there's no symlink
2649 # under the checkout .git/, recreate the symlink.
2650 if name in self.working_tree_dirs or name in self.working_tree_files:
2651 if os.path.exists(src_path) and not os.path.exists(dst_path):
2652 platform_utils.symlink(
2653 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2654
2655 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002656 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002657 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002658 # Fail if the links are pointing to the wrong place
2659 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002660 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002661 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002662 'work tree. If you\'re comfortable with the '
2663 'possibility of losing the work tree\'s git metadata,'
2664 ' use `repo sync --force-sync {0}` to '
2665 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002666
David James8d201162013-10-11 17:03:19 -07002667 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2668 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2669
2670 Args:
2671 gitdir: The bare git repository. Must already be initialized.
2672 dotgit: The repository you would like to initialize.
2673 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2674 Only one work tree can store refs under a given |gitdir|.
2675 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2676 This saves you the effort of initializing |dotgit| yourself.
2677 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002678 symlink_files = self.shareable_files[:]
2679 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002680 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002681 symlink_files += self.working_tree_files
2682 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002683 to_symlink = symlink_files + symlink_dirs
2684
2685 to_copy = []
2686 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002687 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002688
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002689 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002690 for name in set(to_copy).union(to_symlink):
2691 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002692 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002693 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002694
Kevin Degi384b3c52014-10-16 16:02:58 -06002695 if os.path.lexists(dst):
2696 continue
David James8d201162013-10-11 17:03:19 -07002697
2698 # If the source dir doesn't exist, create an empty dir.
2699 if name in symlink_dirs and not os.path.lexists(src):
2700 os.makedirs(src)
2701
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002702 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002703 platform_utils.symlink(
2704 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002705 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002706 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002707 shutil.copytree(src, dst)
2708 elif os.path.isfile(src):
2709 shutil.copy(src, dst)
2710
Conley Owens80b87fe2014-05-09 17:13:44 -07002711 # If the source file doesn't exist, ensure the destination
2712 # file doesn't either.
2713 if name in symlink_files and not os.path.lexists(src):
2714 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002715 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002716 except OSError:
2717 pass
2718
David James8d201162013-10-11 17:03:19 -07002719 except OSError as e:
2720 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002721 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002722 else:
2723 raise
2724
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002725 def _InitGitWorktree(self):
2726 """Init the project using git worktrees."""
2727 self.bare_git.worktree('prune')
2728 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2729 self.worktree, self.GetRevisionId())
2730
2731 # Rewrite the internal state files to use relative paths between the
2732 # checkouts & worktrees.
2733 dotgit = os.path.join(self.worktree, '.git')
2734 with open(dotgit, 'r') as fp:
2735 # Figure out the checkout->worktree path.
2736 setting = fp.read()
2737 assert setting.startswith('gitdir:')
2738 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002739 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2740 # of file permissions. Delete it and recreate it from scratch to avoid.
2741 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002742 # Use relative path from checkout->worktree & maintain Unix line endings
2743 # on all OS's to match git behavior.
2744 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002745 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2746 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002747 # Use relative path from worktree->checkout & maintain Unix line endings
2748 # on all OS's to match git behavior.
2749 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002750 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2751
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002752 self._InitMRef()
2753
Martin Kellye4e94d22017-03-21 16:05:12 -07002754 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002755 realdotgit = os.path.join(self.worktree, '.git')
2756 tmpdotgit = realdotgit + '.tmp'
2757 init_dotgit = not os.path.exists(realdotgit)
2758 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002759 if self.use_git_worktrees:
2760 self._InitGitWorktree()
2761 self._CopyAndLinkFiles()
2762 return
2763
Mike Frysingerf4545122019-11-11 04:34:16 -05002764 dotgit = tmpdotgit
2765 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2766 os.makedirs(tmpdotgit)
2767 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2768 copy_all=False)
2769 else:
2770 dotgit = realdotgit
2771
Kevin Degib1a07b82015-07-27 13:33:43 -06002772 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002773 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2774 except GitError as e:
2775 if force_sync and not init_dotgit:
2776 try:
2777 platform_utils.rmtree(dotgit)
2778 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002779 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002780 raise e
2781 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002782
Mike Frysingerf4545122019-11-11 04:34:16 -05002783 if init_dotgit:
2784 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002785
Mike Frysingerf4545122019-11-11 04:34:16 -05002786 # Now that the .git dir is fully set up, move it to its final home.
2787 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002788
Mike Frysingerf4545122019-11-11 04:34:16 -05002789 # Finish checking out the worktree.
2790 cmd = ['read-tree', '--reset', '-u']
2791 cmd.append('-v')
2792 cmd.append(HEAD)
2793 if GitCommand(self, cmd).Wait() != 0:
2794 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002795
Mike Frysingerf4545122019-11-11 04:34:16 -05002796 if submodules:
2797 self._SyncSubmodules(quiet=True)
2798 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002799
Renaud Paquay788e9622017-01-27 11:41:12 -08002800 def _get_symlink_error_message(self):
2801 if platform_utils.isWindows():
2802 return ('Unable to create symbolic link. Please re-run the command as '
2803 'Administrator, or see '
2804 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2805 'for other options.')
2806 return 'filesystem must support symlinks'
2807
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002809 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002810
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002811 def _revlist(self, *args, **kw):
2812 a = []
2813 a.extend(args)
2814 a.append('--')
2815 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002816
2817 @property
2818 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002819 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002820
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002821 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002822 """Get logs between two revisions of this project."""
2823 comp = '..'
2824 if rev1:
2825 revs = [rev1]
2826 if rev2:
2827 revs.extend([comp, rev2])
2828 cmd = ['log', ''.join(revs)]
2829 out = DiffColoring(self.config)
2830 if out.is_on and color:
2831 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002832 if pretty_format is not None:
2833 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002834 if oneline:
2835 cmd.append('--oneline')
2836
2837 try:
2838 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2839 if log.Wait() == 0:
2840 return log.stdout
2841 except GitError:
2842 # worktree may not exist if groups changed for example. In that case,
2843 # try in gitdir instead.
2844 if not os.path.exists(self.worktree):
2845 return self.bare_git.log(*cmd[1:])
2846 else:
2847 raise
2848 return None
2849
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002850 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2851 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002852 """Get the list of logs from this revision to given revisionId"""
2853 logs = {}
2854 selfId = self.GetRevisionId(self._allrefs)
2855 toId = toProject.GetRevisionId(toProject._allrefs)
2856
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002857 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2858 pretty_format=pretty_format)
2859 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2860 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002861 return logs
2862
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002863 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002864
David James8d201162013-10-11 17:03:19 -07002865 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002866 self._project = project
2867 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002868 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002869
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002870 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2871 def __getstate__(self):
2872 return (self._project, self._bare, self._gitdir)
2873
2874 def __setstate__(self, state):
2875 self._project, self._bare, self._gitdir = state
2876
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002877 def LsOthers(self):
2878 p = GitCommand(self._project,
2879 ['ls-files',
2880 '-z',
2881 '--others',
2882 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002883 bare=False,
David James8d201162013-10-11 17:03:19 -07002884 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002885 capture_stdout=True,
2886 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002887 if p.Wait() == 0:
2888 out = p.stdout
2889 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002890 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002891 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892 return []
2893
2894 def DiffZ(self, name, *args):
2895 cmd = [name]
2896 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002897 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002898 cmd.extend(args)
2899 p = GitCommand(self._project,
2900 cmd,
David James8d201162013-10-11 17:03:19 -07002901 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002902 bare=False,
2903 capture_stdout=True,
2904 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002905 p.Wait()
2906 r = {}
2907 out = p.stdout
2908 if out:
2909 out = iter(out[:-1].split('\0'))
2910 while out:
2911 try:
2912 info = next(out)
2913 path = next(out)
2914 except StopIteration:
2915 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002916
Mike Frysinger84230002021-02-16 17:08:35 -05002917 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002918
Mike Frysinger84230002021-02-16 17:08:35 -05002919 def __init__(self, path, omode, nmode, oid, nid, state):
2920 self.path = path
2921 self.src_path = None
2922 self.old_mode = omode
2923 self.new_mode = nmode
2924 self.old_id = oid
2925 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002926
Mike Frysinger84230002021-02-16 17:08:35 -05002927 if len(state) == 1:
2928 self.status = state
2929 self.level = None
2930 else:
2931 self.status = state[:1]
2932 self.level = state[1:]
2933 while self.level.startswith('0'):
2934 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002935
Mike Frysinger84230002021-02-16 17:08:35 -05002936 info = info[1:].split(' ')
2937 info = _Info(path, *info)
2938 if info.status in ('R', 'C'):
2939 info.src_path = info.path
2940 info.path = next(out)
2941 r[info.path] = info
2942 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002944 def GetDotgitPath(self, subpath=None):
2945 """Return the full path to the .git dir.
2946
2947 As a convenience, append |subpath| if provided.
2948 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002949 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002950 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002951 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002952 dotgit = os.path.join(self._project.worktree, '.git')
2953 if os.path.isfile(dotgit):
2954 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2955 with open(dotgit) as fp:
2956 setting = fp.read()
2957 assert setting.startswith('gitdir:')
2958 gitdir = setting.split(':', 1)[1].strip()
2959 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2960
2961 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2962
2963 def GetHead(self):
2964 """Return the ref that HEAD points to."""
2965 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002966 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002967 with open(path) as fd:
2968 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002969 except IOError as e:
2970 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002971 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302972 line = line.decode()
2973 except AttributeError:
2974 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002975 if line.startswith('ref: '):
2976 return line[5:-1]
2977 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002978
2979 def SetHead(self, ref, message=None):
2980 cmdv = []
2981 if message is not None:
2982 cmdv.extend(['-m', message])
2983 cmdv.append(HEAD)
2984 cmdv.append(ref)
2985 self.symbolic_ref(*cmdv)
2986
2987 def DetachHead(self, new, message=None):
2988 cmdv = ['--no-deref']
2989 if message is not None:
2990 cmdv.extend(['-m', message])
2991 cmdv.append(HEAD)
2992 cmdv.append(new)
2993 self.update_ref(*cmdv)
2994
2995 def UpdateRef(self, name, new, old=None,
2996 message=None,
2997 detach=False):
2998 cmdv = []
2999 if message is not None:
3000 cmdv.extend(['-m', message])
3001 if detach:
3002 cmdv.append('--no-deref')
3003 cmdv.append(name)
3004 cmdv.append(new)
3005 if old is not None:
3006 cmdv.append(old)
3007 self.update_ref(*cmdv)
3008
3009 def DeleteRef(self, name, old=None):
3010 if not old:
3011 old = self.rev_parse(name)
3012 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003013 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003014
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003015 def rev_list(self, *args, **kw):
3016 if 'format' in kw:
3017 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3018 else:
3019 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003020 cmdv.extend(args)
3021 p = GitCommand(self._project,
3022 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003023 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003024 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003025 capture_stdout=True,
3026 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003027 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003028 raise GitError('%s rev-list %s: %s' %
3029 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003030 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003031
3032 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003033 """Allow arbitrary git commands using pythonic syntax.
3034
3035 This allows you to do things like:
3036 git_obj.rev_parse('HEAD')
3037
3038 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3039 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003040 Any other positional arguments will be passed to the git command, and the
3041 following keyword arguments are supported:
3042 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003043
3044 Args:
3045 name: The name of the git command to call. Any '_' characters will
3046 be replaced with '-'.
3047
3048 Returns:
3049 A callable object that will try to call git with the named command.
3050 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003051 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003052
Dave Borowitz091f8932012-10-23 17:01:04 -07003053 def runner(*args, **kwargs):
3054 cmdv = []
3055 config = kwargs.pop('config', None)
3056 for k in kwargs:
3057 raise TypeError('%s() got an unexpected keyword argument %r'
3058 % (name, k))
3059 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303060 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003061 cmdv.append('-c')
3062 cmdv.append('%s=%s' % (k, v))
3063 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003064 cmdv.extend(args)
3065 p = GitCommand(self._project,
3066 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003067 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003068 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003069 capture_stdout=True,
3070 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003071 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003072 raise GitError('%s %s: %s' %
3073 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003074 r = p.stdout
3075 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3076 return r[:-1]
3077 return r
3078 return runner
3079
3080
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003081class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003082
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003083 def __str__(self):
3084 return 'prior sync failed; rebase still in progress'
3085
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003086
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003087class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003088
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003089 def __str__(self):
3090 return 'contains uncommitted changes'
3091
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003092
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003093class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003094
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003095 def __init__(self, project, text):
3096 self.project = project
3097 self.text = text
3098
3099 def Print(self, syncbuf):
3100 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3101 syncbuf.out.nl()
3102
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003103
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003104class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003105
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106 def __init__(self, project, why):
3107 self.project = project
3108 self.why = why
3109
3110 def Print(self, syncbuf):
3111 syncbuf.out.fail('error: %s/: %s',
3112 self.project.relpath,
3113 str(self.why))
3114 syncbuf.out.nl()
3115
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003116
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003117class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003118
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003119 def __init__(self, project, action):
3120 self.project = project
3121 self.action = action
3122
3123 def Run(self, syncbuf):
3124 out = syncbuf.out
3125 out.project('project %s/', self.project.relpath)
3126 out.nl()
3127 try:
3128 self.action()
3129 out.nl()
3130 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003131 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003132 out.nl()
3133 return False
3134
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003135
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003136class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003137
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003138 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003139 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003140 self.project = self.printer('header', attr='bold')
3141 self.info = self.printer('info')
3142 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003143
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003144
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003145class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003146
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003147 def __init__(self, config, detach_head=False):
3148 self._messages = []
3149 self._failures = []
3150 self._later_queue1 = []
3151 self._later_queue2 = []
3152
3153 self.out = _SyncColoring(config)
3154 self.out.redirect(sys.stderr)
3155
3156 self.detach_head = detach_head
3157 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003158 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003159
3160 def info(self, project, fmt, *args):
3161 self._messages.append(_InfoMessage(project, fmt % args))
3162
3163 def fail(self, project, err=None):
3164 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003165 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003166
3167 def later1(self, project, what):
3168 self._later_queue1.append(_Later(project, what))
3169
3170 def later2(self, project, what):
3171 self._later_queue2.append(_Later(project, what))
3172
3173 def Finish(self):
3174 self._PrintMessages()
3175 self._RunLater()
3176 self._PrintMessages()
3177 return self.clean
3178
David Rileye0684ad2017-04-05 00:02:59 -07003179 def Recently(self):
3180 recent_clean = self.recent_clean
3181 self.recent_clean = True
3182 return recent_clean
3183
3184 def _MarkUnclean(self):
3185 self.clean = False
3186 self.recent_clean = False
3187
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003188 def _RunLater(self):
3189 for q in ['_later_queue1', '_later_queue2']:
3190 if not self._RunQueue(q):
3191 return
3192
3193 def _RunQueue(self, queue):
3194 for m in getattr(self, queue):
3195 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003196 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003197 return False
3198 setattr(self, queue, [])
3199 return True
3200
3201 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003202 if self._messages or self._failures:
3203 if os.isatty(2):
3204 self.out.write(progress.CSI_ERASE_LINE)
3205 self.out.write('\r')
3206
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003207 for m in self._messages:
3208 m.Print(self)
3209 for m in self._failures:
3210 m.Print(self)
3211
3212 self._messages = []
3213 self._failures = []
3214
3215
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003216class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003217
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003218 """A special project housed under .repo.
3219 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003220
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003221 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003222 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003223 manifest=manifest,
3224 name=name,
3225 gitdir=gitdir,
3226 objdir=gitdir,
3227 worktree=worktree,
3228 remote=RemoteSpec('origin'),
3229 relpath='.repo/%s' % name,
3230 revisionExpr='refs/heads/master',
3231 revisionId=None,
3232 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003233
3234 def PreSync(self):
3235 if self.Exists:
3236 cb = self.CurrentBranch
3237 if cb:
3238 base = self.GetBranch(cb).merge
3239 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003240 self.revisionExpr = base
3241 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003242
Martin Kelly224a31a2017-07-10 14:46:25 -07003243 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003244 """ Prepare MetaProject for manifest branch switch
3245 """
3246
3247 # detach and delete manifest branch, allowing a new
3248 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003249 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003250 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003251 syncbuf.Finish()
3252
3253 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003254 ['update-ref', '-d', 'refs/heads/default'],
3255 capture_stdout=True,
3256 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003257
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003258 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003259 def LastFetch(self):
3260 try:
3261 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3262 return os.path.getmtime(fh)
3263 except OSError:
3264 return 0
3265
3266 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003267 def HasChanges(self):
3268 """Has the remote received new commits not yet checked out?
3269 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003270 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003271 return False
3272
David Pursehouse8a68ff92012-09-24 12:15:13 +09003273 all_refs = self.bare_ref.all
3274 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003275 head = self.work_git.GetHead()
3276 if head.startswith(R_HEADS):
3277 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003278 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003279 except KeyError:
3280 head = None
3281
3282 if revid == head:
3283 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003284 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003285 return True
3286 return False