blob: 9ff9df0b74dd1ab66d946b34bc3c2cbd8e7f6a40 [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
Jack Neus6ea0cae2021-07-20 20:52:33 +0000254class 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
Jack Neus6ea0cae2021-07-20 20:52:33 +0000261 def __eq__(self, other):
262 if not isinstance(other, Annotation):
263 return False
264 return self.__dict__ == other.__dict__
265
266 def __lt__(self, other):
267 # This exists just so that lists of Annotation objects can be sorted, for
268 # use in comparisons.
269 if not isinstance(other, Annotation):
270 raise ValueError('comparison is not between two Annotation objects')
271 if self.name == other.name:
272 if self.value == other.value:
273 return self.keep < other.keep
274 return self.value < other.value
275 return self.name < other.name
276
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700277
Mike Frysingere6a202f2019-08-02 15:57:57 -0400278def _SafeExpandPath(base, subpath, skipfinal=False):
279 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700280
Mike Frysingere6a202f2019-08-02 15:57:57 -0400281 We make sure no intermediate symlinks are traversed, and that the final path
282 is not a special file (e.g. not a socket or fifo).
283
284 NB: We rely on a number of paths already being filtered out while parsing the
285 manifest. See the validation logic in manifest_xml.py for more details.
286 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500287 # Split up the path by its components. We can't use os.path.sep exclusively
288 # as some platforms (like Windows) will convert / to \ and that bypasses all
289 # our constructed logic here. Especially since manifest authors only use
290 # / in their paths.
291 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
292 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400293 if skipfinal:
294 # Whether the caller handles the final component itself.
295 finalpart = components.pop()
296
297 path = base
298 for part in components:
299 if part in {'.', '..'}:
300 raise ManifestInvalidPathError(
301 '%s: "%s" not allowed in paths' % (subpath, part))
302
303 path = os.path.join(path, part)
304 if platform_utils.islink(path):
305 raise ManifestInvalidPathError(
306 '%s: traversing symlinks not allow' % (path,))
307
308 if os.path.exists(path):
309 if not os.path.isfile(path) and not platform_utils.isdir(path):
310 raise ManifestInvalidPathError(
311 '%s: only regular files & directories allowed' % (path,))
312
313 if skipfinal:
314 path = os.path.join(path, finalpart)
315
316 return path
317
318
319class _CopyFile(object):
320 """Container for <copyfile> manifest element."""
321
322 def __init__(self, git_worktree, src, topdir, dest):
323 """Register a <copyfile> request.
324
325 Args:
326 git_worktree: Absolute path to the git project checkout.
327 src: Relative path under |git_worktree| of file to read.
328 topdir: Absolute path to the top of the repo client checkout.
329 dest: Relative path under |topdir| of file to write.
330 """
331 self.git_worktree = git_worktree
332 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333 self.src = src
334 self.dest = dest
335
336 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400337 src = _SafeExpandPath(self.git_worktree, self.src)
338 dest = _SafeExpandPath(self.topdir, self.dest)
339
340 if platform_utils.isdir(src):
341 raise ManifestInvalidPathError(
342 '%s: copying from directory not supported' % (self.src,))
343 if platform_utils.isdir(dest):
344 raise ManifestInvalidPathError(
345 '%s: copying to directory not allowed' % (self.dest,))
346
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347 # copy file if it does not exist or is out of date
348 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
349 try:
350 # remove existing file first, since it might be read-only
351 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800352 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400353 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200354 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700355 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200356 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700357 shutil.copy(src, dest)
358 # make the file read-only
359 mode = os.stat(dest)[stat.ST_MODE]
360 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
361 os.chmod(dest, mode)
362 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700363 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700364
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700365
Anthony King7bdac712014-07-16 12:56:40 +0100366class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400367 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700368
Mike Frysingere6a202f2019-08-02 15:57:57 -0400369 def __init__(self, git_worktree, src, topdir, dest):
370 """Register a <linkfile> request.
371
372 Args:
373 git_worktree: Absolute path to the git project checkout.
374 src: Target of symlink relative to path under |git_worktree|.
375 topdir: Absolute path to the top of the repo client checkout.
376 dest: Relative path under |topdir| of symlink to create.
377 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700378 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400379 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500380 self.src = src
381 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500382
Wink Saville4c426ef2015-06-03 08:05:17 -0700383 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500384 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700385 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500386 try:
387 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800388 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800389 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500390 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700391 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700392 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500393 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700394 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500395 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700396 _error('Cannot link file %s to %s', relSrc, absDest)
397
398 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400399 """Link the self.src & self.dest paths.
400
401 Handles wild cards on the src linking all of the files in the source in to
402 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700403 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500404 # Some people use src="." to create stable links to projects. Lets allow
405 # that but reject all other uses of "." to keep things simple.
406 if self.src == '.':
407 src = self.git_worktree
408 else:
409 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400410
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300411 if not glob.has_magic(src):
412 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400413 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
414 # dest & src are absolute paths at this point. Make sure the target of
415 # the symlink is relative in the context of the repo client checkout.
416 relpath = os.path.relpath(src, os.path.dirname(dest))
417 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700418 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400419 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300420 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400421 if os.path.exists(dest) and not platform_utils.isdir(dest):
422 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700423 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400424 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700425 # Create a releative path from source dir to destination dir
426 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400427 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700428
429 # Get the source file name
430 srcFile = os.path.basename(absSrcFile)
431
432 # Now form the final full paths to srcFile. They will be
433 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400434 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700435 relSrc = os.path.join(relSrcDir, srcFile)
436 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500437
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700438
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700439class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700440
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700441 def __init__(self,
442 name,
Anthony King7bdac712014-07-16 12:56:40 +0100443 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700444 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100445 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700446 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700447 orig_name=None,
448 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700449 self.name = name
450 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700451 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700452 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100453 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700454 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700455 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456
Ian Kasprzak0286e312021-02-05 10:06:18 -0800457
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700458class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600459 # These objects can be shared between several working trees.
460 shareable_files = ['description', 'info']
461 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
462 # These objects can only be used by a single working tree.
463 working_tree_files = ['config', 'packed-refs', 'shallow']
464 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700465
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700466 def __init__(self,
467 manifest,
468 name,
469 remote,
470 gitdir,
David James8d201162013-10-11 17:03:19 -0700471 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700472 worktree,
473 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700474 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800475 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100476 rebase=True,
477 groups=None,
478 sync_c=False,
479 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900480 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100481 clone_depth=None,
482 upstream=None,
483 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500484 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100485 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900486 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700487 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600488 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700489 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800490 """Init a Project object.
491
492 Args:
493 manifest: The XmlManifest object.
494 name: The `name` attribute of manifest.xml's project element.
495 remote: RemoteSpec object specifying its remote's properties.
496 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700497 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800498 worktree: Absolute path of git working tree.
499 relpath: Relative path of git working tree to repo's top directory.
500 revisionExpr: The `revision` attribute of manifest.xml's project element.
501 revisionId: git commit id for checking out.
502 rebase: The `rebase` attribute of manifest.xml's project element.
503 groups: The `groups` attribute of manifest.xml's project element.
504 sync_c: The `sync-c` attribute of manifest.xml's project element.
505 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900506 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800507 upstream: The `upstream` attribute of manifest.xml's project element.
508 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500509 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800510 is_derived: False if the project was explicitly defined in the manifest;
511 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400512 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900513 optimized_fetch: If True, when a project is set to a sha1 revision, only
514 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600515 retry_fetches: Retry remote fetches n times upon receiving transient error
516 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700517 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800518 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400519 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700520 self.name = name
521 self.remote = remote
Michael Kelly37c21c22020-06-13 02:10:40 -0700522 self.UpdatePaths(relpath, worktree, gitdir, objdir)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700523 self.revisionExpr = revisionExpr
524
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700525 if revisionId is None \
526 and revisionExpr \
527 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700528 self.revisionId = revisionExpr
529 else:
530 self.revisionId = revisionId
531
Mike Pontillod3153822012-02-28 11:53:24 -0800532 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700533 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700534 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800535 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900536 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900537 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700538 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800539 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500540 # NB: Do not use this setting in __init__ to change behavior so that the
541 # manifest.git checkout can inspect & change it after instantiating. See
542 # the XmlManifest init code for more info.
543 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800544 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900545 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600546 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800547 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800548
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700549 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700550 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500551 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500552 self.annotations = []
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
Michael Kelly37c21c22020-06-13 02:10:40 -0700560 def UpdatePaths(self, relpath, worktree, gitdir, objdir):
561 """Update paths used by this project"""
562 self.gitdir = gitdir.replace('\\', '/')
563 self.objdir = objdir.replace('\\', '/')
564 if worktree:
565 self.worktree = os.path.normpath(worktree).replace('\\', '/')
566 else:
567 self.worktree = None
568 self.relpath = relpath
569
570 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
571 defaults=self.manifest.globalConfig)
572
573 if self.worktree:
574 self.work_git = self._GitGetByExec(self, bare=False, gitdir=self.gitdir)
575 else:
576 self.work_git = None
577 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=self.gitdir)
578 self.bare_ref = GitRefs(self.gitdir)
579 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=self.objdir)
580
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700581 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800582 def Derived(self):
583 return self.is_derived
584
585 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700586 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700587 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700588
589 @property
590 def CurrentBranch(self):
591 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400592
593 The branch name omits the 'refs/heads/' prefix.
594 None is returned if the project is on a detached HEAD, or if the work_git is
595 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700596 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400597 try:
598 b = self.work_git.GetHead()
599 except NoManifestException:
600 # If the local checkout is in a bad state, don't barf. Let the callers
601 # process this like the head is unreadable.
602 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700603 if b.startswith(R_HEADS):
604 return b[len(R_HEADS):]
605 return None
606
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700607 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500608 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
609 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
610 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200611
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700612 def IsDirty(self, consider_untracked=True):
613 """Is the working directory modified in some way?
614 """
615 self.work_git.update_index('-q',
616 '--unmerged',
617 '--ignore-missing',
618 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900619 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700620 return True
621 if self.work_git.DiffZ('diff-files'):
622 return True
623 if consider_untracked and self.work_git.LsOthers():
624 return True
625 return False
626
627 _userident_name = None
628 _userident_email = None
629
630 @property
631 def UserName(self):
632 """Obtain the user's personal name.
633 """
634 if self._userident_name is None:
635 self._LoadUserIdentity()
636 return self._userident_name
637
638 @property
639 def UserEmail(self):
640 """Obtain the user's email address. This is very likely
641 to be their Gerrit login.
642 """
643 if self._userident_email is None:
644 self._LoadUserIdentity()
645 return self._userident_email
646
647 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900648 u = self.bare_git.var('GIT_COMMITTER_IDENT')
649 m = re.compile("^(.*) <([^>]*)> ").match(u)
650 if m:
651 self._userident_name = m.group(1)
652 self._userident_email = m.group(2)
653 else:
654 self._userident_name = ''
655 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700656
657 def GetRemote(self, name):
658 """Get the configuration for a single remote.
659 """
660 return self.config.GetRemote(name)
661
662 def GetBranch(self, name):
663 """Get the configuration for a single branch.
664 """
665 return self.config.GetBranch(name)
666
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700667 def GetBranches(self):
668 """Get all existing local branches.
669 """
670 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900671 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700672 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700673
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530674 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700675 if name.startswith(R_HEADS):
676 name = name[len(R_HEADS):]
677 b = self.GetBranch(name)
678 b.current = name == current
679 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900680 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700681 heads[name] = b
682
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530683 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700684 if name.startswith(R_PUB):
685 name = name[len(R_PUB):]
686 b = heads.get(name)
687 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900688 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700689
690 return heads
691
Colin Cross5acde752012-03-28 20:15:45 -0700692 def MatchesGroups(self, manifest_groups):
693 """Returns true if the manifest groups specified at init should cause
694 this project to be synced.
695 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700696 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700697
698 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700699 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700700 manifest_groups: "-group1,group2"
701 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500702
703 The special manifest group "default" will match any project that
704 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700705 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500706 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700707 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700708 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500709 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700710
Conley Owens971de8e2012-04-16 10:36:08 -0700711 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700712 for group in expanded_manifest_groups:
713 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700714 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700715 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700716 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700717
Conley Owens971de8e2012-04-16 10:36:08 -0700718 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700719
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700720# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700721 def UncommitedFiles(self, get_all=True):
722 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700723
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 Args:
725 get_all: a boolean, if True - get information about all different
726 uncommitted files. If False - return as soon as any kind of
727 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500728 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700729 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500730 self.work_git.update_index('-q',
731 '--unmerged',
732 '--ignore-missing',
733 '--refresh')
734 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700735 details.append("rebase in progress")
736 if not get_all:
737 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500738
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700739 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
740 if changes:
741 details.extend(changes)
742 if not get_all:
743 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500744
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700745 changes = self.work_git.DiffZ('diff-files').keys()
746 if changes:
747 details.extend(changes)
748 if not get_all:
749 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500750
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700751 changes = self.work_git.LsOthers()
752 if changes:
753 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500754
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700755 return details
756
757 def HasChanges(self):
758 """Returns true if there are uncommitted changes.
759 """
760 if self.UncommitedFiles(get_all=False):
761 return True
762 else:
763 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500764
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600765 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700766 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200767
768 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200769 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600770 quiet: If True then only print the project name. Do not print
771 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700772 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700773 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700774 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200775 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700776 print(file=output_redir)
777 print('project %s/' % self.relpath, file=output_redir)
778 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700779 return
780
781 self.work_git.update_index('-q',
782 '--unmerged',
783 '--ignore-missing',
784 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700785 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700786 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
787 df = self.work_git.DiffZ('diff-files')
788 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100789 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700790 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791
792 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700793 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200794 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700795 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700796
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600797 if quiet:
798 out.nl()
799 return 'DIRTY'
800
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801 branch = self.CurrentBranch
802 if branch is None:
803 out.nobranch('(*** NO BRANCH ***)')
804 else:
805 out.branch('branch %s', branch)
806 out.nl()
807
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700808 if rb:
809 out.important('prior sync failed; rebase still in progress')
810 out.nl()
811
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812 paths = list()
813 paths.extend(di.keys())
814 paths.extend(df.keys())
815 paths.extend(do)
816
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530817 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900818 try:
819 i = di[p]
820 except KeyError:
821 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900823 try:
824 f = df[p]
825 except KeyError:
826 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200827
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900828 if i:
829 i_status = i.status.upper()
830 else:
831 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700832
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900833 if f:
834 f_status = f.status.lower()
835 else:
836 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700837
838 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800839 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700840 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700841 else:
842 line = ' %s%s\t%s' % (i_status, f_status, p)
843
844 if i and not f:
845 out.added('%s', line)
846 elif (i and f) or (not i and f):
847 out.changed('%s', line)
848 elif not i and not f:
849 out.untracked('%s', line)
850 else:
851 out.write('%s', line)
852 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200853
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700854 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700855
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500856 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 """Prints the status of the repository to stdout.
858 """
859 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500860 if output_redir:
861 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862 cmd = ['diff']
863 if out.is_on:
864 cmd.append('--color')
865 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300866 if absolute_paths:
867 cmd.append('--src-prefix=a/%s/' % self.relpath)
868 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400870 try:
871 p = GitCommand(self,
872 cmd,
873 capture_stdout=True,
874 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500875 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400876 except GitError as e:
877 out.nl()
878 out.project('project %s/' % self.relpath)
879 out.nl()
880 out.fail('%s', str(e))
881 out.nl()
882 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500883 if p.stdout:
884 out.nl()
885 out.project('project %s/' % self.relpath)
886 out.nl()
Mike Frysinger9888acc2021-03-09 11:31:14 -0500887 out.write('%s', p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400888 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700889
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700890# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900891 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892 """Was the branch published (uploaded) for code review?
893 If so, returns the SHA-1 hash of the last published
894 state for the branch.
895 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700896 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900897 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700898 try:
899 return self.bare_git.rev_parse(key)
900 except GitError:
901 return None
902 else:
903 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900904 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700905 except KeyError:
906 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907
David Pursehouse8a68ff92012-09-24 12:15:13 +0900908 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700909 """Prunes any stale published refs.
910 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900911 if all_refs is None:
912 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700913 heads = set()
914 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530915 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916 if name.startswith(R_HEADS):
917 heads.add(name)
918 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900919 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530921 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 n = name[len(R_PUB):]
923 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900924 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700925
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700926 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 """List any branches which can be uploaded for review.
928 """
929 heads = {}
930 pubed = {}
931
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530932 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900934 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900936 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700937
938 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530939 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900940 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700941 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700942 if selected_branch and branch != selected_branch:
943 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800945 rb = self.GetUploadableBranch(branch)
946 if rb:
947 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700948 return ready
949
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800950 def GetUploadableBranch(self, branch_name):
951 """Get a single uploadable branch, or None.
952 """
953 branch = self.GetBranch(branch_name)
954 base = branch.LocalMerge
955 if branch.LocalMerge:
956 rb = ReviewableBranch(self, branch, base)
957 if rb.commits:
958 return rb
959 return None
960
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700961 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100962 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500963 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700964 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500965 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500966 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200967 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700968 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200969 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200970 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800971 validate_certs=True,
972 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973 """Uploads the named branch for code review.
974 """
975 if branch is None:
976 branch = self.CurrentBranch
977 if branch is None:
978 raise GitError('not currently on a branch')
979
980 branch = self.GetBranch(branch)
981 if not branch.LocalMerge:
982 raise GitError('branch %s does not track a remote' % branch.name)
983 if not branch.remote.review:
984 raise GitError('remote %s has no review url' % branch.remote.name)
985
Bryan Jacobsf609f912013-05-06 13:36:24 -0400986 if dest_branch is None:
987 dest_branch = self.dest_branch
988 if dest_branch is None:
989 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700990 if not dest_branch.startswith(R_HEADS):
991 dest_branch = R_HEADS + dest_branch
992
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800993 if not branch.remote.projectname:
994 branch.remote.projectname = self.name
995 branch.remote.Save()
996
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200997 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800998 if url is None:
999 raise UploadError('review not configured')
1000 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -05001001 if dryrun:
1002 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001003
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001004 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001005 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001006
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001007 for push_option in (push_options or []):
1008 cmd.append('-o')
1009 cmd.append(push_option)
1010
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001011 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001012
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001013 if dest_branch.startswith(R_HEADS):
1014 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001015
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001016 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001017 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001018 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001019 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001020 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001021 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001022
David Pursehousef25a3702018-11-14 19:01:22 -08001023 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001024 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001025 if notify:
1026 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001027 if private:
1028 opts += ['private']
1029 if wip:
1030 opts += ['wip']
1031 if opts:
1032 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001033 cmd.append(ref_spec)
1034
Anthony King7bdac712014-07-16 12:56:40 +01001035 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001036 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037
Mike Frysingerd7f86832020-11-19 19:18:46 -05001038 if not dryrun:
1039 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1040 self.bare_git.UpdateRef(R_PUB + branch.name,
1041 R_HEADS + branch.name,
1042 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001044# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001045 def _ExtractArchive(self, tarpath, path=None):
1046 """Extract the given tar on its current location
1047
1048 Args:
1049 - tarpath: The path to the actual tar file
1050
1051 """
1052 try:
1053 with tarfile.open(tarpath, 'r') as tar:
1054 tar.extractall(path=path)
1055 return True
1056 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001057 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001058 return False
1059
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001060 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001061 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001062 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05001063 output_redir=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001064 is_new=None,
Mike Frysinger73561142021-05-03 01:10:09 -04001065 current_branch_only=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001066 force_sync=False,
1067 clone_bundle=True,
Mike Frysingerd68ed632021-05-03 01:21:35 -04001068 tags=None,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001069 archive=False,
1070 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001071 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001072 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001073 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001074 ssh_proxy=None,
Raman Tennetif32f2432021-04-12 20:57:25 -07001075 clone_filter=None,
Raman Tenneticd89ec12021-04-22 09:18:14 -07001076 partial_clone_exclude=set()):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 """Perform only the network IO portion of the sync process.
1078 Local working directory/branch state is not affected.
1079 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001080 if archive and not isinstance(self, MetaProject):
1081 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001082 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001083 return False
1084
1085 name = self.relpath.replace('\\', '/')
1086 name = name.replace('/', '_')
1087 tarpath = '%s.tar' % name
1088 topdir = self.manifest.topdir
1089
1090 try:
1091 self._FetchArchive(tarpath, cwd=topdir)
1092 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001093 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001094 return False
1095
1096 # From now on, we only need absolute tarpath
1097 tarpath = os.path.join(topdir, tarpath)
1098
1099 if not self._ExtractArchive(tarpath, path=topdir):
1100 return False
1101 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001102 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001103 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001104 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001105 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001106 return True
Mike Frysinger76844ba2021-02-28 17:08:55 -05001107
1108 # If the shared object dir already exists, don't try to rebootstrap with a
1109 # clone bundle download. We should have the majority of objects already.
1110 if clone_bundle and os.path.exists(self.objdir):
1111 clone_bundle = False
1112
Raman Tennetif32f2432021-04-12 20:57:25 -07001113 if self.name in partial_clone_exclude:
1114 clone_bundle = True
1115 clone_filter = None
1116
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001117 if is_new is None:
1118 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001119 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001120 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001121 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001122 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001123 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001124
1125 if is_new:
1126 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1127 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001128 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001129 # This works for both absolute and relative alternate directories.
1130 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001131 except IOError:
1132 alt_dir = None
1133 else:
1134 alt_dir = None
1135
Mike Frysingere50b6a72020-02-19 01:45:48 -05001136 if (clone_bundle
1137 and alt_dir is None
1138 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001139 is_new = False
1140
Mike Frysinger73561142021-05-03 01:10:09 -04001141 if current_branch_only is None:
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001142 if self.sync_c:
1143 current_branch_only = True
1144 elif not self.manifest._loaded:
1145 # Manifest cannot check defaults until it syncs.
1146 current_branch_only = False
1147 elif self.manifest.default.sync_c:
1148 current_branch_only = True
1149
Mike Frysingerd68ed632021-05-03 01:21:35 -04001150 if tags is None:
1151 tags = self.sync_tags
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001152
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001153 if self.clone_depth:
1154 depth = self.clone_depth
1155 else:
1156 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1157
Mike Frysinger521d01b2020-02-17 01:51:49 -05001158 # See if we can skip the network fetch entirely.
1159 if not (optimized_fetch and
1160 (ID_RE.match(self.revisionExpr) and
1161 self._CheckForImmutableRevision())):
1162 if not self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05001163 initial=is_new,
1164 quiet=quiet, verbose=verbose, output_redir=output_redir,
1165 alt_dir=alt_dir, current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001166 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001167 submodules=submodules, force_sync=force_sync,
Mike Frysinger19e409c2021-05-05 19:44:35 -04001168 ssh_proxy=ssh_proxy,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001169 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001170 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001171
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001172 mp = self.manifest.manifestProject
1173 dissociate = mp.config.GetBoolean('repo.dissociate')
1174 if dissociate:
1175 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1176 if os.path.exists(alternates_file):
1177 cmd = ['repack', '-a', '-d']
Mike Frysinger7b586f22021-02-23 18:38:39 -05001178 p = GitCommand(self, cmd, bare=True, capture_stdout=bool(output_redir),
1179 merge_output=bool(output_redir))
1180 if p.stdout and output_redir:
Mike Frysinger3c093122021-03-03 11:17:27 -05001181 output_redir.write(p.stdout)
Mike Frysinger7b586f22021-02-23 18:38:39 -05001182 if p.Wait() != 0:
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001183 return False
1184 platform_utils.remove(alternates_file)
1185
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001186 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001187 self._InitMRef()
1188 else:
1189 self._InitMirrorHead()
Mike Frysinger9d96f582021-09-28 11:27:24 -04001190 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'),
1191 missing_ok=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001193
1194 def PostRepoUpgrade(self):
1195 self._InitHooks()
1196
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001197 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001198 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001199 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001200 for copyfile in self.copyfiles:
1201 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001202 for linkfile in self.linkfiles:
1203 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001204
Julien Camperguedd654222014-01-09 16:21:37 +01001205 def GetCommitRevisionId(self):
1206 """Get revisionId of a commit.
1207
1208 Use this method instead of GetRevisionId to get the id of the commit rather
1209 than the id of the current git object (for example, a tag)
1210
1211 """
1212 if not self.revisionExpr.startswith(R_TAGS):
1213 return self.GetRevisionId(self._allrefs)
1214
1215 try:
1216 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1217 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001218 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1219 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001220
David Pursehouse8a68ff92012-09-24 12:15:13 +09001221 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001222 if self.revisionId:
1223 return self.revisionId
1224
1225 rem = self.GetRemote(self.remote.name)
1226 rev = rem.ToLocal(self.revisionExpr)
1227
David Pursehouse8a68ff92012-09-24 12:15:13 +09001228 if all_refs is not None and rev in all_refs:
1229 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001230
1231 try:
1232 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1233 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001234 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1235 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001236
Raman Tenneti6a872c92021-01-14 19:17:50 -08001237 def SetRevisionId(self, revisionId):
Xin Li0e776a52021-06-29 21:42:34 +00001238 if self.revisionExpr:
Xin Liaabf79d2021-04-29 01:50:38 -07001239 self.upstream = self.revisionExpr
1240
Raman Tenneti6a872c92021-01-14 19:17:50 -08001241 self.revisionId = revisionId
1242
Martin Kellye4e94d22017-03-21 16:05:12 -07001243 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001244 """Perform only the local IO portion of the sync process.
1245 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001246 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001247 if not os.path.exists(self.gitdir):
1248 syncbuf.fail(self,
1249 'Cannot checkout %s due to missing network sync; Run '
1250 '`repo sync -n %s` first.' %
1251 (self.name, self.name))
1252 return
1253
Martin Kellye4e94d22017-03-21 16:05:12 -07001254 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001255 all_refs = self.bare_ref.all
1256 self.CleanPublishedCache(all_refs)
1257 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001258
Mike Frysinger0458faa2021-03-10 23:35:44 -05001259 # Special case the root of the repo client checkout. Make sure it doesn't
1260 # contain files being checked out to dirs we don't allow.
1261 if self.relpath == '.':
1262 PROTECTED_PATHS = {'.repo'}
1263 paths = set(self.work_git.ls_tree('-z', '--name-only', '--', revid).split('\0'))
1264 bad_paths = paths & PROTECTED_PATHS
1265 if bad_paths:
1266 syncbuf.fail(self,
1267 'Refusing to checkout project that writes to protected '
1268 'paths: %s' % (', '.join(bad_paths),))
1269 return
1270
David Pursehouse1d947b32012-10-25 12:23:11 +09001271 def _doff():
1272 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001273 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001274
Martin Kellye4e94d22017-03-21 16:05:12 -07001275 def _dosubmodules():
1276 self._SyncSubmodules(quiet=True)
1277
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001278 head = self.work_git.GetHead()
1279 if head.startswith(R_HEADS):
1280 branch = head[len(R_HEADS):]
1281 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001282 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001283 except KeyError:
1284 head = None
1285 else:
1286 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001287
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001288 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001289 # Currently on a detached HEAD. The user is assumed to
1290 # not have any local modifications worth worrying about.
1291 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001292 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001293 syncbuf.fail(self, _PriorSyncFailedError())
1294 return
1295
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001296 if head == revid:
1297 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001298 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001299 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001300 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001301 # The copy/linkfile config may have changed.
1302 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001303 return
1304 else:
1305 lost = self._revlist(not_rev(revid), HEAD)
1306 if lost:
1307 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001308
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001309 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001310 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001311 if submodules:
1312 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001313 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001314 syncbuf.fail(self, e)
1315 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001316 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001317 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001318
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001319 if head == revid:
1320 # No changes; don't do anything further.
1321 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001322 # The copy/linkfile config may have changed.
1323 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001324 return
1325
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001326 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001327
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001328 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001329 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001330 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001331 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001332 syncbuf.info(self,
1333 "leaving %s; does not track upstream",
1334 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001335 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001336 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001337 if submodules:
1338 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001339 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001340 syncbuf.fail(self, e)
1341 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001342 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001343 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001344
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001345 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001346
1347 # See if we can perform a fast forward merge. This can happen if our
1348 # branch isn't in the exact same state as we last published.
1349 try:
1350 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1351 # Skip the published logic.
1352 pub = False
1353 except GitError:
1354 pub = self.WasPublished(branch.name, all_refs)
1355
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001356 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001357 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001358 if not_merged:
1359 if upstream_gain:
1360 # The user has published this branch and some of those
1361 # commits are not yet merged upstream. We do not want
1362 # to rewrite the published commits so we punt.
1363 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001364 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001365 "branch %s is published (but not merged) and is now "
1366 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001367 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001368 elif pub == head:
1369 # All published commits are merged, and thus we are a
1370 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001371 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001372 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001373 if submodules:
1374 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001375 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001376
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001377 # Examine the local commits not in the remote. Find the
1378 # last one attributed to this user, if any.
1379 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001380 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001381 last_mine = None
1382 cnt_mine = 0
1383 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001384 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001385 if committer_email == self.UserEmail:
1386 last_mine = commit_id
1387 cnt_mine += 1
1388
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001389 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001390 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001391
1392 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001393 syncbuf.fail(self, _DirtyError())
1394 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001395
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001396 # If the upstream switched on us, warn the user.
1397 #
1398 if branch.merge != self.revisionExpr:
1399 if branch.merge and self.revisionExpr:
1400 syncbuf.info(self,
1401 'manifest switched %s...%s',
1402 branch.merge,
1403 self.revisionExpr)
1404 elif branch.merge:
1405 syncbuf.info(self,
1406 'manifest no longer tracks %s',
1407 branch.merge)
1408
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001409 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001410 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001411 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001412 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001413 syncbuf.info(self,
1414 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001415 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001416
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001417 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001418 if not ID_RE.match(self.revisionExpr):
1419 # in case of manifest sync the revisionExpr might be a SHA1
1420 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001421 if not branch.merge.startswith('refs/'):
1422 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423 branch.Save()
1424
Mike Pontillod3153822012-02-28 11:53:24 -08001425 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001426 def _docopyandlink():
1427 self._CopyAndLinkFiles()
1428
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001429 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001430 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001431 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001432 if submodules:
1433 syncbuf.later2(self, _dosubmodules)
1434 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001435 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001436 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001437 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001438 if submodules:
1439 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001440 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001441 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001442 syncbuf.fail(self, e)
1443 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001444 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001445 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001446 if submodules:
1447 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001448
Mike Frysingere6a202f2019-08-02 15:57:57 -04001449 def AddCopyFile(self, src, dest, topdir):
1450 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001451
Mike Frysingere6a202f2019-08-02 15:57:57 -04001452 No filesystem changes occur here. Actual copying happens later on.
1453
1454 Paths should have basic validation run on them before being queued.
1455 Further checking will be handled when the actual copy happens.
1456 """
1457 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1458
1459 def AddLinkFile(self, src, dest, topdir):
1460 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1461
1462 No filesystem changes occur here. Actual linking happens later on.
1463
1464 Paths should have basic validation run on them before being queued.
1465 Further checking will be handled when the actual link happens.
1466 """
1467 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001468
James W. Mills24c13082012-04-12 15:04:13 -05001469 def AddAnnotation(self, name, value, keep):
Jack Neus6ea0cae2021-07-20 20:52:33 +00001470 self.annotations.append(Annotation(name, value, keep))
James W. Mills24c13082012-04-12 15:04:13 -05001471
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001472 def DownloadPatchSet(self, change_id, patch_id):
1473 """Download a single patch set of a single change to FETCH_HEAD.
1474 """
1475 remote = self.GetRemote(self.remote.name)
1476
1477 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001478 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001479 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001480 if GitCommand(self, cmd, bare=True).Wait() != 0:
1481 return None
1482 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001483 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001484 change_id,
1485 patch_id,
1486 self.bare_git.rev_parse('FETCH_HEAD'))
1487
Mike Frysingerc0d18662020-02-19 19:19:18 -05001488 def DeleteWorktree(self, quiet=False, force=False):
1489 """Delete the source checkout and any other housekeeping tasks.
1490
1491 This currently leaves behind the internal .repo/ cache state. This helps
1492 when switching branches or manifest changes get reverted as we don't have
1493 to redownload all the git objects. But we should do some GC at some point.
1494
1495 Args:
1496 quiet: Whether to hide normal messages.
1497 force: Always delete tree even if dirty.
1498
1499 Returns:
1500 True if the worktree was completely cleaned out.
1501 """
1502 if self.IsDirty():
1503 if force:
1504 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1505 (self.relpath,), file=sys.stderr)
1506 else:
1507 print('error: %s: Cannot remove project: uncommitted changes are '
1508 'present.\n' % (self.relpath,), file=sys.stderr)
1509 return False
1510
1511 if not quiet:
1512 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1513
1514 # Unlock and delink from the main worktree. We don't use git's worktree
1515 # remove because it will recursively delete projects -- we handle that
1516 # ourselves below. https://crbug.com/git/48
1517 if self.use_git_worktrees:
1518 needle = platform_utils.realpath(self.gitdir)
1519 # Find the git worktree commondir under .repo/worktrees/.
1520 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1521 assert output.startswith('worktree '), output
1522 commondir = output[9:]
1523 # Walk each of the git worktrees to see where they point.
1524 configs = os.path.join(commondir, 'worktrees')
1525 for name in os.listdir(configs):
1526 gitdir = os.path.join(configs, name, 'gitdir')
1527 with open(gitdir) as fp:
1528 relpath = fp.read().strip()
1529 # Resolve the checkout path and see if it matches this project.
1530 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1531 if fullpath == needle:
1532 platform_utils.rmtree(os.path.join(configs, name))
1533
1534 # Delete the .git directory first, so we're less likely to have a partially
1535 # working git repository around. There shouldn't be any git projects here,
1536 # so rmtree works.
1537
1538 # Try to remove plain files first in case of git worktrees. If this fails
1539 # for any reason, we'll fall back to rmtree, and that'll display errors if
1540 # it can't remove things either.
1541 try:
1542 platform_utils.remove(self.gitdir)
1543 except OSError:
1544 pass
1545 try:
1546 platform_utils.rmtree(self.gitdir)
1547 except OSError as e:
1548 if e.errno != errno.ENOENT:
1549 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1550 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1551 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1552 return False
1553
1554 # Delete everything under the worktree, except for directories that contain
1555 # another git project.
1556 dirs_to_remove = []
1557 failed = False
1558 for root, dirs, files in platform_utils.walk(self.worktree):
1559 for f in files:
1560 path = os.path.join(root, f)
1561 try:
1562 platform_utils.remove(path)
1563 except OSError as e:
1564 if e.errno != errno.ENOENT:
1565 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1566 failed = True
1567 dirs[:] = [d for d in dirs
1568 if not os.path.lexists(os.path.join(root, d, '.git'))]
1569 dirs_to_remove += [os.path.join(root, d) for d in dirs
1570 if os.path.join(root, d) not in dirs_to_remove]
1571 for d in reversed(dirs_to_remove):
1572 if platform_utils.islink(d):
1573 try:
1574 platform_utils.remove(d)
1575 except OSError as e:
1576 if e.errno != errno.ENOENT:
1577 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1578 failed = True
1579 elif not platform_utils.listdir(d):
1580 try:
1581 platform_utils.rmdir(d)
1582 except OSError as e:
1583 if e.errno != errno.ENOENT:
1584 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1585 failed = True
1586 if failed:
1587 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1588 file=sys.stderr)
1589 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1590 return False
1591
1592 # Try deleting parent dirs if they are empty.
1593 path = self.worktree
1594 while path != self.manifest.topdir:
1595 try:
1596 platform_utils.rmdir(path)
1597 except OSError as e:
1598 if e.errno != errno.ENOENT:
1599 break
1600 path = os.path.dirname(path)
1601
1602 return True
1603
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001604# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001605 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001606 """Create a new branch off the manifest's revision.
1607 """
Simran Basib9a1b732015-08-20 12:19:28 -07001608 if not branch_merge:
1609 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001610 head = self.work_git.GetHead()
1611 if head == (R_HEADS + name):
1612 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001613
David Pursehouse8a68ff92012-09-24 12:15:13 +09001614 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001615 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001616 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001617 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001618 capture_stdout=True,
1619 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001620
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001621 branch = self.GetBranch(name)
1622 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001623 branch.merge = branch_merge
1624 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1625 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001626
1627 if revision is None:
1628 revid = self.GetRevisionId(all_refs)
1629 else:
1630 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001631
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001632 if head.startswith(R_HEADS):
1633 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001634 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001635 except KeyError:
1636 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001637 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001638 ref = R_HEADS + name
1639 self.work_git.update_ref(ref, revid)
1640 self.work_git.symbolic_ref(HEAD, ref)
1641 branch.Save()
1642 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001643
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001644 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001645 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001646 capture_stdout=True,
1647 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001648 branch.Save()
1649 return True
1650 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001651
Wink Saville02d79452009-04-10 13:01:24 -07001652 def CheckoutBranch(self, name):
1653 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001654
1655 Args:
1656 name: The name of the branch to checkout.
1657
1658 Returns:
1659 True if the checkout succeeded; False if it didn't; None if the branch
1660 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001661 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001662 rev = R_HEADS + name
1663 head = self.work_git.GetHead()
1664 if head == rev:
1665 # Already on the branch
1666 #
1667 return True
Wink Saville02d79452009-04-10 13:01:24 -07001668
David Pursehouse8a68ff92012-09-24 12:15:13 +09001669 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001670 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001671 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001672 except KeyError:
1673 # Branch does not exist in this project
1674 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001675 return None
Wink Saville02d79452009-04-10 13:01:24 -07001676
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001677 if head.startswith(R_HEADS):
1678 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001679 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001680 except KeyError:
1681 head = None
1682
1683 if head == revid:
1684 # Same revision; just update HEAD to point to the new
1685 # target branch, but otherwise take no other action.
1686 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001687 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1688 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001689 return True
1690
1691 return GitCommand(self,
1692 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001693 capture_stdout=True,
1694 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001695
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001696 def AbandonBranch(self, name):
1697 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001698
1699 Args:
1700 name: The name of the branch to abandon.
1701
1702 Returns:
1703 True if the abandon succeeded; False if it didn't; None if the branch
1704 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001705 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001706 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001707 all_refs = self.bare_ref.all
1708 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001709 # Doesn't exist
1710 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001711
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001712 head = self.work_git.GetHead()
1713 if head == rev:
1714 # We can't destroy the branch while we are sitting
1715 # on it. Switch to a detached HEAD.
1716 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001717 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001718
David Pursehouse8a68ff92012-09-24 12:15:13 +09001719 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001720 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001721 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001722 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001723 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001724
1725 return GitCommand(self,
1726 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001727 capture_stdout=True,
1728 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001729
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001730 def PruneHeads(self):
1731 """Prune any topic branches already merged into upstream.
1732 """
1733 cb = self.CurrentBranch
1734 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001735 left = self._allrefs
1736 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001737 if name.startswith(R_HEADS):
1738 name = name[len(R_HEADS):]
1739 if cb is None or name != cb:
1740 kill.append(name)
1741
Mike Frysingera3794e92021-03-11 23:24:01 -05001742 # Minor optimization: If there's nothing to prune, then don't try to read
1743 # any project state.
1744 if not kill and not cb:
1745 return []
1746
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001747 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001748 if cb is not None \
1749 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001750 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001751 self.work_git.DetachHead(HEAD)
1752 kill.append(cb)
1753
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001754 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001755 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001756
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001757 try:
1758 self.bare_git.DetachHead(rev)
1759
1760 b = ['branch', '-d']
1761 b.extend(kill)
1762 b = GitCommand(self, b, bare=True,
1763 capture_stdout=True,
1764 capture_stderr=True)
1765 b.Wait()
1766 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001767 if ID_RE.match(old):
1768 self.bare_git.DetachHead(old)
1769 else:
1770 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001771 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001772
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001773 for branch in kill:
1774 if (R_HEADS + branch) not in left:
1775 self.CleanPublishedCache()
1776 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001777
1778 if cb and cb not in kill:
1779 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001780 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001781
1782 kept = []
1783 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001784 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001785 branch = self.GetBranch(branch)
1786 base = branch.LocalMerge
1787 if not base:
1788 base = rev
1789 kept.append(ReviewableBranch(self, branch, base))
1790 return kept
1791
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001792# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001793 def GetRegisteredSubprojects(self):
1794 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001795
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001796 def rec(subprojects):
1797 if not subprojects:
1798 return
1799 result.extend(subprojects)
1800 for p in subprojects:
1801 rec(p.subprojects)
1802 rec(self.subprojects)
1803 return result
1804
1805 def _GetSubmodules(self):
1806 # Unfortunately we cannot call `git submodule status --recursive` here
1807 # because the working tree might not exist yet, and it cannot be used
1808 # without a working tree in its current implementation.
1809
1810 def get_submodules(gitdir, rev):
1811 # Parse .gitmodules for submodule sub_paths and sub_urls
1812 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1813 if not sub_paths:
1814 return []
1815 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1816 # revision of submodule repository
1817 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1818 submodules = []
1819 for sub_path, sub_url in zip(sub_paths, sub_urls):
1820 try:
1821 sub_rev = sub_revs[sub_path]
1822 except KeyError:
1823 # Ignore non-exist submodules
1824 continue
1825 submodules.append((sub_rev, sub_path, sub_url))
1826 return submodules
1827
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001828 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1829 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001830
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001831 def parse_gitmodules(gitdir, rev):
1832 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1833 try:
Anthony King7bdac712014-07-16 12:56:40 +01001834 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1835 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001836 except GitError:
1837 return [], []
1838 if p.Wait() != 0:
1839 return [], []
1840
1841 gitmodules_lines = []
1842 fd, temp_gitmodules_path = tempfile.mkstemp()
1843 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001844 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001845 os.close(fd)
1846 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001847 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1848 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001849 if p.Wait() != 0:
1850 return [], []
1851 gitmodules_lines = p.stdout.split('\n')
1852 except GitError:
1853 return [], []
1854 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001855 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001856
1857 names = set()
1858 paths = {}
1859 urls = {}
1860 for line in gitmodules_lines:
1861 if not line:
1862 continue
1863 m = re_path.match(line)
1864 if m:
1865 names.add(m.group(1))
1866 paths[m.group(1)] = m.group(2)
1867 continue
1868 m = re_url.match(line)
1869 if m:
1870 names.add(m.group(1))
1871 urls[m.group(1)] = m.group(2)
1872 continue
1873 names = sorted(names)
1874 return ([paths.get(name, '') for name in names],
1875 [urls.get(name, '') for name in names])
1876
1877 def git_ls_tree(gitdir, rev, paths):
1878 cmd = ['ls-tree', rev, '--']
1879 cmd.extend(paths)
1880 try:
Anthony King7bdac712014-07-16 12:56:40 +01001881 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1882 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001883 except GitError:
1884 return []
1885 if p.Wait() != 0:
1886 return []
1887 objects = {}
1888 for line in p.stdout.split('\n'):
1889 if not line.strip():
1890 continue
1891 object_rev, object_path = line.split()[2:4]
1892 objects[object_path] = object_rev
1893 return objects
1894
1895 try:
1896 rev = self.GetRevisionId()
1897 except GitError:
1898 return []
1899 return get_submodules(self.gitdir, rev)
1900
1901 def GetDerivedSubprojects(self):
1902 result = []
1903 if not self.Exists:
1904 # If git repo does not exist yet, querying its submodules will
1905 # mess up its states; so return here.
1906 return result
1907 for rev, path, url in self._GetSubmodules():
1908 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001909 relpath, worktree, gitdir, objdir = \
1910 self.manifest.GetSubprojectPaths(self, name, path)
1911 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001912 if project:
1913 result.extend(project.GetDerivedSubprojects())
1914 continue
David James8d201162013-10-11 17:03:19 -07001915
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001916 if url.startswith('..'):
1917 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001918 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001919 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001920 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001921 review=self.remote.review,
1922 revision=self.remote.revision)
1923 subproject = Project(manifest=self.manifest,
1924 name=name,
1925 remote=remote,
1926 gitdir=gitdir,
1927 objdir=objdir,
1928 worktree=worktree,
1929 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001930 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001931 revisionId=rev,
1932 rebase=self.rebase,
1933 groups=self.groups,
1934 sync_c=self.sync_c,
1935 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001936 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001937 parent=self,
1938 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001939 result.append(subproject)
1940 result.extend(subproject.GetDerivedSubprojects())
1941 return result
1942
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001943# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001944 def EnableRepositoryExtension(self, key, value='true', version=1):
1945 """Enable git repository extension |key| with |value|.
1946
1947 Args:
1948 key: The extension to enabled. Omit the "extensions." prefix.
1949 value: The value to use for the extension.
1950 version: The minimum git repository version needed.
1951 """
1952 # Make sure the git repo version is new enough already.
1953 found_version = self.config.GetInt('core.repositoryFormatVersion')
1954 if found_version is None:
1955 found_version = 0
1956 if found_version < version:
1957 self.config.SetString('core.repositoryFormatVersion', str(version))
1958
1959 # Enable the extension!
1960 self.config.SetString('extensions.%s' % (key,), value)
1961
Mike Frysinger50a81de2020-09-06 15:51:21 -04001962 def ResolveRemoteHead(self, name=None):
1963 """Find out what the default branch (HEAD) points to.
1964
1965 Normally this points to refs/heads/master, but projects are moving to main.
1966 Support whatever the server uses rather than hardcoding "master" ourselves.
1967 """
1968 if name is None:
1969 name = self.remote.name
1970
1971 # The output will look like (NB: tabs are separators):
1972 # ref: refs/heads/master HEAD
1973 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1974 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1975
1976 for line in output.splitlines():
1977 lhs, rhs = line.split('\t', 1)
1978 if rhs == 'HEAD' and lhs.startswith('ref:'):
1979 return lhs[4:].strip()
1980
1981 return None
1982
Zac Livingstone4332262017-06-16 08:56:09 -06001983 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001984 try:
1985 # if revision (sha or tag) is not present then following function
1986 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001987 self.bare_git.rev_list('-1', '--missing=allow-any',
1988 '%s^0' % self.revisionExpr, '--')
Xin Li0e776a52021-06-29 21:42:34 +00001989 if self.upstream:
1990 rev = self.GetRemote(self.remote.name).ToLocal(self.upstream)
1991 self.bare_git.rev_list('-1', '--missing=allow-any',
1992 '%s^0' % rev, '--')
Xin Li8e983bb2021-07-20 20:15:30 +00001993 self.bare_git.merge_base('--is-ancestor', self.revisionExpr, rev)
Chris AtLee2fb64662014-01-16 21:32:33 -05001994 return True
1995 except GitError:
1996 # There is no such persistent revision. We have to fetch it.
1997 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001998
Julien Campergue335f5ef2013-10-16 11:02:35 +02001999 def _FetchArchive(self, tarpath, cwd=None):
2000 cmd = ['archive', '-v', '-o', tarpath]
2001 cmd.append('--remote=%s' % self.remote.url)
2002 cmd.append('--prefix=%s/' % self.relpath)
2003 cmd.append(self.revisionExpr)
2004
2005 command = GitCommand(self, cmd, cwd=cwd,
2006 capture_stdout=True,
2007 capture_stderr=True)
2008
2009 if command.Wait() != 0:
2010 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2011
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002012 def _RemoteFetch(self, name=None,
2013 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002014 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002015 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002016 verbose=False,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002017 output_redir=None,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002018 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002019 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002020 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002021 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002022 submodules=False,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002023 ssh_proxy=None,
Xin Li745be2e2019-06-03 11:24:30 -07002024 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002025 clone_filter=None,
2026 retry_fetches=2,
2027 retry_sleep_initial_sec=4.0,
2028 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002029 is_sha1 = False
2030 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002031 # The depth should not be used when fetching to a mirror because
2032 # it will result in a shallow repository that cannot be cloned or
2033 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002034 # The repo project should also never be synced with partial depth.
2035 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2036 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002037
Shawn Pearce69e04d82014-01-29 12:48:54 -08002038 if depth:
2039 current_branch_only = True
2040
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002041 if ID_RE.match(self.revisionExpr) is not None:
2042 is_sha1 = True
2043
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002044 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002045 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002046 # this is a tag and its sha1 value should never change
2047 tag_name = self.revisionExpr[len(R_TAGS):]
2048
2049 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002050 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002051 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002052 print('Skipped fetching project %s (already have persistent ref)'
2053 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002054 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002055 if is_sha1 and not depth:
2056 # When syncing a specific commit and --depth is not set:
2057 # * if upstream is explicitly specified and is not a sha1, fetch only
2058 # upstream as users expect only upstream to be fetch.
2059 # Note: The commit might not be in upstream in which case the sync
2060 # will fail.
2061 # * otherwise, fetch all branches to make sure we end up with the
2062 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002063 if self.upstream:
2064 current_branch_only = not ID_RE.match(self.upstream)
2065 else:
2066 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002067
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002068 if not name:
2069 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002070
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002071 remote = self.GetRemote(name)
Mike Frysinger339f2df2021-05-06 00:44:42 -04002072 if not remote.PreConnectFetch(ssh_proxy):
2073 ssh_proxy = None
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002074
Shawn O. Pearce88443382010-10-08 10:02:09 +02002075 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002076 if alt_dir and 'objects' == os.path.basename(alt_dir):
2077 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002078 packed_refs = os.path.join(self.gitdir, 'packed-refs')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002079
David Pursehouse8a68ff92012-09-24 12:15:13 +09002080 all_refs = self.bare_ref.all
2081 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002082 tmp = set()
2083
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302084 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002085 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002086 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002087 all_refs[r] = ref_id
2088 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002089 continue
2090
David Pursehouse8a68ff92012-09-24 12:15:13 +09002091 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002092 continue
2093
David Pursehouse8a68ff92012-09-24 12:15:13 +09002094 r = 'refs/_alt/%s' % ref_id
2095 all_refs[r] = ref_id
2096 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002097 tmp.add(r)
2098
heping3d7bbc92017-04-12 19:51:47 +08002099 tmp_packed_lines = []
2100 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002101
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302102 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002103 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002104 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002105 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002106 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002107
heping3d7bbc92017-04-12 19:51:47 +08002108 tmp_packed = ''.join(tmp_packed_lines)
2109 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002110 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002112 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002113
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002114 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002115
Xin Li745be2e2019-06-03 11:24:30 -07002116 if clone_filter:
2117 git_require((2, 19, 0), fail=True, msg='partial clones')
2118 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002119 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002120
Conley Owensf97e8382015-01-21 11:12:46 -08002121 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002122 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002123 else:
2124 # If this repo has shallow objects, then we don't know which refs have
2125 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2126 # do this with projects that don't have shallow objects, since it is less
2127 # efficient.
2128 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2129 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002130
Mike Frysinger4847e052020-02-22 00:07:35 -05002131 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002132 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002133 if not quiet and sys.stdout.isatty():
2134 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002135 if not self.worktree:
2136 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002137 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002138
Mike Frysingere57f1142019-03-18 21:27:54 -04002139 if force_sync:
2140 cmd.append('--force')
2141
David Pursehouse74cfd272015-10-14 10:50:15 +09002142 if prune:
2143 cmd.append('--prune')
2144
Martin Kellye4e94d22017-03-21 16:05:12 -07002145 if submodules:
2146 cmd.append('--recurse-submodules=on-demand')
2147
Kuang-che Wu6856f982019-11-25 12:37:55 +08002148 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002149 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002150 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002151 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002152 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002153 spec.append('tag')
2154 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002155
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302156 if self.manifest.IsMirror and not current_branch_only:
2157 branch = None
2158 else:
2159 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002160 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002161 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002162 # Shallow checkout of a specific commit, fetch from that commit and not
2163 # the heads only as the commit might be deeper in the history.
2164 spec.append(branch)
Xin Liaabf79d2021-04-29 01:50:38 -07002165 if self.upstream:
2166 spec.append(self.upstream)
Kuang-che Wu6856f982019-11-25 12:37:55 +08002167 else:
2168 if is_sha1:
2169 branch = self.upstream
2170 if branch is not None and branch.strip():
2171 if not branch.startswith('refs/'):
2172 branch = R_HEADS + branch
2173 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2174
2175 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2176 # whole repo.
2177 if self.manifest.IsMirror and not spec:
2178 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2179
2180 # If using depth then we should not get all the tags since they may
2181 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002182 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002183 cmd.append('--no-tags')
2184 else:
2185 cmd.append('--tags')
2186 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2187
Conley Owens80b87fe2014-05-09 17:13:44 -07002188 cmd.extend(spec)
2189
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002190 # At least one retry minimum due to git remote prune.
2191 retry_fetches = max(retry_fetches, 2)
2192 retry_cur_sleep = retry_sleep_initial_sec
2193 ok = prune_tried = False
2194 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002195 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger7b586f22021-02-23 18:38:39 -05002196 merge_output=True, capture_stdout=quiet or bool(output_redir))
2197 if gitcmd.stdout and not quiet and output_redir:
2198 output_redir.write(gitcmd.stdout)
John L. Villalovos126e2982015-01-29 21:58:12 -08002199 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002200 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002201 ok = True
2202 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002203
2204 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002205 elif (gitcmd.stdout and
2206 'error:' in gitcmd.stdout and
2207 'HTTP 429' in gitcmd.stdout):
Mike Frysinger6823bc22021-04-15 02:06:28 -04002208 # Fallthru to sleep+retry logic at the bottom.
2209 pass
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002210
Mike Frysinger6823bc22021-04-15 02:06:28 -04002211 # Try to prune remote branches once in case there are conflicts.
2212 # For example, if the remote had refs/heads/upstream, but deleted that and
2213 # now has refs/heads/upstream/foo.
2214 elif (gitcmd.stdout and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002215 'error:' in gitcmd.stdout and
2216 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002217 not prune_tried):
2218 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002219 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002220 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002221 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002222 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002223 break
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002224 print('retrying fetch after pruning remote branches', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002225 # Continue right away so we don't sleep as we shouldn't need to.
John L. Villalovos126e2982015-01-29 21:58:12 -08002226 continue
Brian Harring14a66742012-09-28 20:21:57 -07002227 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002228 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2229 # in sha1 mode, we just tried sync'ing from the upstream field; it
2230 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002231 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002232 elif ret < 0:
2233 # Git died with a signal, exit immediately
2234 break
Mike Frysinger6823bc22021-04-15 02:06:28 -04002235
2236 # Figure out how long to sleep before the next attempt, if there is one.
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002237 if not verbose and gitcmd.stdout:
2238 print('\n%s:\n%s' % (self.name, gitcmd.stdout), end='', file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002239 if try_n < retry_fetches - 1:
Mike Frysingerb16b9d22021-05-20 01:48:17 -04002240 print('%s: sleeping %s seconds before retrying' % (self.name, retry_cur_sleep),
2241 file=output_redir)
Mike Frysinger6823bc22021-04-15 02:06:28 -04002242 time.sleep(retry_cur_sleep)
2243 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2244 MAXIMUM_RETRY_SLEEP_SEC)
2245 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2246 RETRY_JITTER_PERCENT))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002247
2248 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002249 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002250 if old_packed != '':
2251 _lwrite(packed_refs, old_packed)
2252 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002253 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002254 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002255
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002256 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002257 # We just synced the upstream given branch; verify we
2258 # got what we wanted, else trigger a second run of all
2259 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002260 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002261 # Sync the current branch only with depth set to None.
2262 # We always pass depth=None down to avoid infinite recursion.
2263 return self._RemoteFetch(
Mike Frysinger7b586f22021-02-23 18:38:39 -05002264 name=name, quiet=quiet, verbose=verbose, output_redir=output_redir,
Mike Frysinger521d01b2020-02-17 01:51:49 -05002265 current_branch_only=current_branch_only and depth,
2266 initial=False, alt_dir=alt_dir,
Mike Frysinger19e409c2021-05-05 19:44:35 -04002267 depth=None, ssh_proxy=ssh_proxy, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002268
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002269 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002270
Mike Frysingere50b6a72020-02-19 01:45:48 -05002271 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002272 if initial and \
2273 (self.manifest.manifestProject.config.GetString('repo.depth') or
2274 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002275 return False
2276
2277 remote = self.GetRemote(self.remote.name)
2278 bundle_url = remote.url + '/clone.bundle'
2279 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002280 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2281 'persistent-http',
2282 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002283 return False
2284
2285 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2286 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2287
2288 exist_dst = os.path.exists(bundle_dst)
2289 exist_tmp = os.path.exists(bundle_tmp)
2290
2291 if not initial and not exist_dst and not exist_tmp:
2292 return False
2293
2294 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002295 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2296 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002297 if not exist_dst:
2298 return False
2299
2300 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002301 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002302 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002303 if not quiet and sys.stdout.isatty():
2304 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002305 if not self.worktree:
2306 cmd.append('--update-head-ok')
2307 cmd.append(bundle_dst)
2308 for f in remote.fetch:
2309 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002310 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002311
2312 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Mike Frysinger9d96f582021-09-28 11:27:24 -04002313 platform_utils.remove(bundle_dst, missing_ok=True)
2314 platform_utils.remove(bundle_tmp, missing_ok=True)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002315 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002316
Mike Frysingere50b6a72020-02-19 01:45:48 -05002317 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002318 platform_utils.remove(dstPath, missing_ok=True)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002319
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002320 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002321 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002322 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002323 if os.path.exists(tmpPath):
2324 size = os.stat(tmpPath).st_size
2325 if size >= 1024:
2326 cmd += ['--continue-at', '%d' % (size,)]
2327 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002328 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002329 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002330 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002331 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002332 if proxy:
2333 cmd += ['--proxy', proxy]
2334 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2335 cmd += ['--proxy', os.environ['http_proxy']]
2336 if srcUrl.startswith('persistent-https'):
2337 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2338 elif srcUrl.startswith('persistent-http'):
2339 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002340 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002341
Dave Borowitz137d0132015-01-02 11:12:54 -08002342 if IsTrace():
2343 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002344 if verbose:
2345 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2346 stdout = None if verbose else subprocess.PIPE
2347 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002348 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002349 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002350 except OSError:
2351 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002352
Mike Frysingere50b6a72020-02-19 01:45:48 -05002353 (output, _) = proc.communicate()
2354 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002355
Dave Borowitz137d0132015-01-02 11:12:54 -08002356 if curlret == 22:
2357 # From curl man page:
2358 # 22: HTTP page not retrieved. The requested url was not found or
2359 # returned another error with the HTTP error code being 400 or above.
2360 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002361 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002362 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2363 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002364 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002365 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002366 elif curlret and not verbose and output:
2367 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002368
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002369 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002370 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002371 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002372 return True
2373 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002374 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002375 return False
2376 else:
2377 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002378
Kris Giesingc8d882a2014-12-23 13:02:32 -08002379 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002380 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002381 with open(path, 'rb') as f:
2382 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002383 return True
2384 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002385 if not quiet:
2386 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002387 return False
2388 except OSError:
2389 return False
2390
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002391 def _Checkout(self, rev, quiet=False):
2392 cmd = ['checkout']
2393 if quiet:
2394 cmd.append('-q')
2395 cmd.append(rev)
2396 cmd.append('--')
2397 if GitCommand(self, cmd).Wait() != 0:
2398 if self._allrefs:
2399 raise GitError('%s checkout %s ' % (self.name, rev))
2400
Mike Frysinger915fda12020-03-22 12:15:20 -04002401 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002402 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002403 if ffonly:
2404 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002405 if record_origin:
2406 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002407 cmd.append(rev)
2408 cmd.append('--')
2409 if GitCommand(self, cmd).Wait() != 0:
2410 if self._allrefs:
2411 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2412
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302413 def _LsRemote(self, refs):
2414 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302415 p = GitCommand(self, cmd, capture_stdout=True)
2416 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002417 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302418 return None
2419
Anthony King7bdac712014-07-16 12:56:40 +01002420 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002421 cmd = ['revert']
2422 cmd.append('--no-edit')
2423 cmd.append(rev)
2424 cmd.append('--')
2425 if GitCommand(self, cmd).Wait() != 0:
2426 if self._allrefs:
2427 raise GitError('%s revert %s ' % (self.name, rev))
2428
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002429 def _ResetHard(self, rev, quiet=True):
2430 cmd = ['reset', '--hard']
2431 if quiet:
2432 cmd.append('-q')
2433 cmd.append(rev)
2434 if GitCommand(self, cmd).Wait() != 0:
2435 raise GitError('%s reset --hard %s ' % (self.name, rev))
2436
Martin Kellye4e94d22017-03-21 16:05:12 -07002437 def _SyncSubmodules(self, quiet=True):
2438 cmd = ['submodule', 'update', '--init', '--recursive']
2439 if quiet:
2440 cmd.append('-q')
2441 if GitCommand(self, cmd).Wait() != 0:
2442 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2443
Anthony King7bdac712014-07-16 12:56:40 +01002444 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002445 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002446 if onto is not None:
2447 cmd.extend(['--onto', onto])
2448 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002449 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002450 raise GitError('%s rebase %s ' % (self.name, upstream))
2451
Pierre Tardy3d125942012-05-04 12:18:12 +02002452 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002453 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002454 if ffonly:
2455 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002456 if GitCommand(self, cmd).Wait() != 0:
2457 raise GitError('%s merge %s ' % (self.name, head))
2458
David Pursehousee8ace262020-02-13 12:41:15 +09002459 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002460 init_git_dir = not os.path.exists(self.gitdir)
2461 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002462 try:
2463 # Initialize the bare repository, which contains all of the objects.
2464 if init_obj_dir:
2465 os.makedirs(self.objdir)
2466 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002467
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002468 if self.use_git_worktrees:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002469 # Enable per-worktree config file support if possible. This is more a
2470 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002471 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002472 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002473
Kevin Degib1a07b82015-07-27 13:33:43 -06002474 # If we have a separate directory to hold refs, initialize it as well.
2475 if self.objdir != self.gitdir:
2476 if init_git_dir:
2477 os.makedirs(self.gitdir)
2478
2479 if init_obj_dir or init_git_dir:
2480 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2481 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002482 try:
2483 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2484 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002485 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002486 print("Retrying clone after deleting %s" %
2487 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002488 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002489 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2490 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002491 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002492 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002493 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2494 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002495 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002496 raise e
2497 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002498
Kevin Degi384b3c52014-10-16 16:02:58 -06002499 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002500 mp = self.manifest.manifestProject
2501 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002502
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 if ref_dir or mirror_git:
2504 if not mirror_git:
2505 mirror_git = os.path.join(ref_dir, self.name + '.git')
2506 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2507 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002508 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2509 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002510
Kevin Degib1a07b82015-07-27 13:33:43 -06002511 if os.path.exists(mirror_git):
2512 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002513 elif os.path.exists(repo_git):
2514 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002515 elif os.path.exists(worktrees_git):
2516 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002517 else:
2518 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002519
Kevin Degib1a07b82015-07-27 13:33:43 -06002520 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002521 if not os.path.isabs(ref_dir):
2522 # The alternate directory is relative to the object database.
2523 ref_dir = os.path.relpath(ref_dir,
2524 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002525 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2526 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002527
David Pursehousee8ace262020-02-13 12:41:15 +09002528 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002529
2530 m = self.manifest.manifestProject.config
2531 for key in ['user.name', 'user.email']:
2532 if m.Has(key, include_defaults=False):
2533 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002534 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002535 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002536 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002537 except Exception:
2538 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002539 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002540 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002541 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002542 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002543
David Pursehousee8ace262020-02-13 12:41:15 +09002544 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002545 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002546 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002547
David Pursehousee8ace262020-02-13 12:41:15 +09002548 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002549 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002550 if not os.path.exists(hooks):
2551 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002552 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002553 name = os.path.basename(stock_hook)
2554
Victor Boivie65e0f352011-04-18 11:23:29 +02002555 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002556 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002557 # Don't install a Gerrit Code Review hook if this
2558 # project does not appear to use it for reviews.
2559 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002560 # Since the manifest project is one of those, but also
2561 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002562 continue
2563
2564 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002565 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002566 continue
2567 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002568 # If the files are the same, we'll leave it alone. We create symlinks
2569 # below by default but fallback to hardlinks if the OS blocks them.
2570 # So if we're here, it's probably because we made a hardlink below.
2571 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002572 if not quiet:
2573 _warn("%s: Not replacing locally modified %s hook",
2574 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002575 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002576 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002577 platform_utils.symlink(
2578 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002579 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002580 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002581 try:
2582 os.link(stock_hook, dst)
2583 except OSError:
2584 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002585 else:
2586 raise
2587
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002588 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002589 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002590 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002591 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002592 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002593 remote.review = self.remote.review
2594 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002596 if self.worktree:
2597 remote.ResetFetch(mirror=False)
2598 else:
2599 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002600 remote.Save()
2601
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002602 def _InitMRef(self):
2603 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002604 if self.use_git_worktrees:
Mike Frysinger29626b42021-05-01 09:37:13 -04002605 # Set up the m/ space to point to the worktree-specific ref space.
2606 # We'll update the worktree-specific ref space on each checkout.
2607 ref = R_M + self.manifest.branch
2608 if not self.bare_ref.symref(ref):
2609 self.bare_git.symbolic_ref(
2610 '-m', 'redirecting to worktree scope',
2611 ref, R_WORKTREE_M + self.manifest.branch)
2612
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002613 # We can't update this ref with git worktrees until it exists.
2614 # We'll wait until the initial checkout to set it.
2615 if not os.path.exists(self.worktree):
2616 return
2617
2618 base = R_WORKTREE_M
2619 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002620
2621 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002622 else:
2623 base = R_M
2624 active_git = self.bare_git
2625
2626 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002627
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002628 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002629 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002630
Remy Böhmer1469c282020-12-15 18:49:02 +01002631 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002632 cur = self.bare_ref.symref(ref)
2633
2634 if self.revisionId:
2635 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2636 msg = 'manifest set to %s' % self.revisionId
2637 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002638 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002639 else:
2640 remote = self.GetRemote(self.remote.name)
2641 dst = remote.ToLocal(self.revisionExpr)
2642 if cur != dst:
2643 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002644 if detach:
2645 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2646 else:
2647 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002648
Kevin Degi384b3c52014-10-16 16:02:58 -06002649 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002650 # Git worktrees don't use symlinks to share at all.
2651 if self.use_git_worktrees:
2652 return
2653
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002654 symlink_files = self.shareable_files[:]
2655 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002656 if share_refs:
2657 symlink_files += self.working_tree_files
2658 symlink_dirs += self.working_tree_dirs
2659 to_symlink = symlink_files + symlink_dirs
2660 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002661 # Try to self-heal a bit in simple cases.
2662 dst_path = os.path.join(destdir, name)
2663 src_path = os.path.join(srcdir, name)
2664
2665 if name in self.working_tree_dirs:
2666 # If the dir is missing under .repo/projects/, create it.
2667 if not os.path.exists(src_path):
2668 os.makedirs(src_path)
2669
2670 elif name in self.working_tree_files:
2671 # If it's a file under the checkout .git/ and the .repo/projects/ has
2672 # nothing, move the file under the .repo/projects/ tree.
2673 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2674 platform_utils.rename(dst_path, src_path)
2675
2676 # If the path exists under the .repo/projects/ and there's no symlink
2677 # under the checkout .git/, recreate the symlink.
2678 if name in self.working_tree_dirs or name in self.working_tree_files:
2679 if os.path.exists(src_path) and not os.path.exists(dst_path):
2680 platform_utils.symlink(
2681 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2682
2683 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002684 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002685 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002686 # Fail if the links are pointing to the wrong place
2687 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002688 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002689 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002690 'work tree. If you\'re comfortable with the '
2691 'possibility of losing the work tree\'s git metadata,'
2692 ' use `repo sync --force-sync {0}` to '
2693 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002694
David James8d201162013-10-11 17:03:19 -07002695 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2696 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2697
2698 Args:
2699 gitdir: The bare git repository. Must already be initialized.
2700 dotgit: The repository you would like to initialize.
2701 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2702 Only one work tree can store refs under a given |gitdir|.
2703 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2704 This saves you the effort of initializing |dotgit| yourself.
2705 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002706 symlink_files = self.shareable_files[:]
2707 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002708 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002709 symlink_files += self.working_tree_files
2710 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002711 to_symlink = symlink_files + symlink_dirs
2712
2713 to_copy = []
2714 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002715 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002716
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002717 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002718 for name in set(to_copy).union(to_symlink):
2719 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002720 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002721 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002722
Kevin Degi384b3c52014-10-16 16:02:58 -06002723 if os.path.lexists(dst):
2724 continue
David James8d201162013-10-11 17:03:19 -07002725
2726 # If the source dir doesn't exist, create an empty dir.
2727 if name in symlink_dirs and not os.path.lexists(src):
2728 os.makedirs(src)
2729
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002730 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002731 platform_utils.symlink(
2732 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002733 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002734 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002735 shutil.copytree(src, dst)
2736 elif os.path.isfile(src):
2737 shutil.copy(src, dst)
2738
Conley Owens80b87fe2014-05-09 17:13:44 -07002739 # If the source file doesn't exist, ensure the destination
2740 # file doesn't either.
2741 if name in symlink_files and not os.path.lexists(src):
Mike Frysinger9d96f582021-09-28 11:27:24 -04002742 platform_utils.remove(dst, missing_ok=True)
Conley Owens80b87fe2014-05-09 17:13:44 -07002743
David James8d201162013-10-11 17:03:19 -07002744 except OSError as e:
2745 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002746 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002747 else:
2748 raise
2749
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002750 def _InitGitWorktree(self):
2751 """Init the project using git worktrees."""
2752 self.bare_git.worktree('prune')
2753 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2754 self.worktree, self.GetRevisionId())
2755
2756 # Rewrite the internal state files to use relative paths between the
2757 # checkouts & worktrees.
2758 dotgit = os.path.join(self.worktree, '.git')
2759 with open(dotgit, 'r') as fp:
2760 # Figure out the checkout->worktree path.
2761 setting = fp.read()
2762 assert setting.startswith('gitdir:')
2763 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002764 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2765 # of file permissions. Delete it and recreate it from scratch to avoid.
2766 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002767 # Use relative path from checkout->worktree & maintain Unix line endings
2768 # on all OS's to match git behavior.
2769 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002770 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2771 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002772 # Use relative path from worktree->checkout & maintain Unix line endings
2773 # on all OS's to match git behavior.
2774 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002775 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2776
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002777 self._InitMRef()
2778
Martin Kellye4e94d22017-03-21 16:05:12 -07002779 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002780 realdotgit = os.path.join(self.worktree, '.git')
2781 tmpdotgit = realdotgit + '.tmp'
2782 init_dotgit = not os.path.exists(realdotgit)
2783 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002784 if self.use_git_worktrees:
2785 self._InitGitWorktree()
2786 self._CopyAndLinkFiles()
2787 return
2788
Mike Frysingerf4545122019-11-11 04:34:16 -05002789 dotgit = tmpdotgit
2790 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2791 os.makedirs(tmpdotgit)
2792 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2793 copy_all=False)
2794 else:
2795 dotgit = realdotgit
2796
Kevin Degib1a07b82015-07-27 13:33:43 -06002797 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002798 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2799 except GitError as e:
2800 if force_sync and not init_dotgit:
2801 try:
2802 platform_utils.rmtree(dotgit)
2803 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002804 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002805 raise e
2806 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807
Mike Frysingerf4545122019-11-11 04:34:16 -05002808 if init_dotgit:
2809 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002810
Mike Frysingerf4545122019-11-11 04:34:16 -05002811 # Now that the .git dir is fully set up, move it to its final home.
2812 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002813
Mike Frysingerf4545122019-11-11 04:34:16 -05002814 # Finish checking out the worktree.
2815 cmd = ['read-tree', '--reset', '-u']
2816 cmd.append('-v')
2817 cmd.append(HEAD)
2818 if GitCommand(self, cmd).Wait() != 0:
2819 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002820
Mike Frysingerf4545122019-11-11 04:34:16 -05002821 if submodules:
2822 self._SyncSubmodules(quiet=True)
2823 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002824
Renaud Paquay788e9622017-01-27 11:41:12 -08002825 def _get_symlink_error_message(self):
2826 if platform_utils.isWindows():
2827 return ('Unable to create symbolic link. Please re-run the command as '
2828 'Administrator, or see '
2829 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2830 'for other options.')
2831 return 'filesystem must support symlinks'
2832
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002833 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002834 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002835
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002836 def _revlist(self, *args, **kw):
2837 a = []
2838 a.extend(args)
2839 a.append('--')
2840 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002841
2842 @property
2843 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002844 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002845
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002846 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002847 """Get logs between two revisions of this project."""
2848 comp = '..'
2849 if rev1:
2850 revs = [rev1]
2851 if rev2:
2852 revs.extend([comp, rev2])
2853 cmd = ['log', ''.join(revs)]
2854 out = DiffColoring(self.config)
2855 if out.is_on and color:
2856 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002857 if pretty_format is not None:
2858 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002859 if oneline:
2860 cmd.append('--oneline')
2861
2862 try:
2863 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2864 if log.Wait() == 0:
2865 return log.stdout
2866 except GitError:
2867 # worktree may not exist if groups changed for example. In that case,
2868 # try in gitdir instead.
2869 if not os.path.exists(self.worktree):
2870 return self.bare_git.log(*cmd[1:])
2871 else:
2872 raise
2873 return None
2874
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002875 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2876 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002877 """Get the list of logs from this revision to given revisionId"""
2878 logs = {}
2879 selfId = self.GetRevisionId(self._allrefs)
2880 toId = toProject.GetRevisionId(toProject._allrefs)
2881
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002882 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2883 pretty_format=pretty_format)
2884 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2885 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002886 return logs
2887
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002888 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002889
David James8d201162013-10-11 17:03:19 -07002890 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002891 self._project = project
2892 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002893 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002894
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002895 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2896 def __getstate__(self):
2897 return (self._project, self._bare, self._gitdir)
2898
2899 def __setstate__(self, state):
2900 self._project, self._bare, self._gitdir = state
2901
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002902 def LsOthers(self):
2903 p = GitCommand(self._project,
2904 ['ls-files',
2905 '-z',
2906 '--others',
2907 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002908 bare=False,
David James8d201162013-10-11 17:03:19 -07002909 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002910 capture_stdout=True,
2911 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002912 if p.Wait() == 0:
2913 out = p.stdout
2914 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002915 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002916 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002917 return []
2918
2919 def DiffZ(self, name, *args):
2920 cmd = [name]
2921 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002922 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002923 cmd.extend(args)
2924 p = GitCommand(self._project,
2925 cmd,
David James8d201162013-10-11 17:03:19 -07002926 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002927 bare=False,
2928 capture_stdout=True,
2929 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002930 p.Wait()
2931 r = {}
2932 out = p.stdout
2933 if out:
2934 out = iter(out[:-1].split('\0'))
2935 while out:
2936 try:
2937 info = next(out)
2938 path = next(out)
2939 except StopIteration:
2940 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002941
Mike Frysinger84230002021-02-16 17:08:35 -05002942 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002943
Mike Frysinger84230002021-02-16 17:08:35 -05002944 def __init__(self, path, omode, nmode, oid, nid, state):
2945 self.path = path
2946 self.src_path = None
2947 self.old_mode = omode
2948 self.new_mode = nmode
2949 self.old_id = oid
2950 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002951
Mike Frysinger84230002021-02-16 17:08:35 -05002952 if len(state) == 1:
2953 self.status = state
2954 self.level = None
2955 else:
2956 self.status = state[:1]
2957 self.level = state[1:]
2958 while self.level.startswith('0'):
2959 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002960
Mike Frysinger84230002021-02-16 17:08:35 -05002961 info = info[1:].split(' ')
2962 info = _Info(path, *info)
2963 if info.status in ('R', 'C'):
2964 info.src_path = info.path
2965 info.path = next(out)
2966 r[info.path] = info
2967 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002968
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002969 def GetDotgitPath(self, subpath=None):
2970 """Return the full path to the .git dir.
2971
2972 As a convenience, append |subpath| if provided.
2973 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002974 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002975 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002976 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002977 dotgit = os.path.join(self._project.worktree, '.git')
2978 if os.path.isfile(dotgit):
2979 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2980 with open(dotgit) as fp:
2981 setting = fp.read()
2982 assert setting.startswith('gitdir:')
2983 gitdir = setting.split(':', 1)[1].strip()
2984 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2985
2986 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2987
2988 def GetHead(self):
2989 """Return the ref that HEAD points to."""
2990 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002991 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002992 with open(path) as fd:
2993 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002994 except IOError as e:
2995 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002996 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302997 line = line.decode()
2998 except AttributeError:
2999 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07003000 if line.startswith('ref: '):
3001 return line[5:-1]
3002 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003003
3004 def SetHead(self, ref, message=None):
3005 cmdv = []
3006 if message is not None:
3007 cmdv.extend(['-m', message])
3008 cmdv.append(HEAD)
3009 cmdv.append(ref)
3010 self.symbolic_ref(*cmdv)
3011
3012 def DetachHead(self, new, message=None):
3013 cmdv = ['--no-deref']
3014 if message is not None:
3015 cmdv.extend(['-m', message])
3016 cmdv.append(HEAD)
3017 cmdv.append(new)
3018 self.update_ref(*cmdv)
3019
3020 def UpdateRef(self, name, new, old=None,
3021 message=None,
3022 detach=False):
3023 cmdv = []
3024 if message is not None:
3025 cmdv.extend(['-m', message])
3026 if detach:
3027 cmdv.append('--no-deref')
3028 cmdv.append(name)
3029 cmdv.append(new)
3030 if old is not None:
3031 cmdv.append(old)
3032 self.update_ref(*cmdv)
3033
3034 def DeleteRef(self, name, old=None):
3035 if not old:
3036 old = self.rev_parse(name)
3037 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07003038 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003039
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07003040 def rev_list(self, *args, **kw):
3041 if 'format' in kw:
3042 cmdv = ['log', '--pretty=format:%s' % kw['format']]
3043 else:
3044 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003045 cmdv.extend(args)
3046 p = GitCommand(self._project,
3047 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003048 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003049 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003050 capture_stdout=True,
3051 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003052 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003053 raise GitError('%s rev-list %s: %s' %
3054 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04003055 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003056
3057 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003058 """Allow arbitrary git commands using pythonic syntax.
3059
3060 This allows you to do things like:
3061 git_obj.rev_parse('HEAD')
3062
3063 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3064 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003065 Any other positional arguments will be passed to the git command, and the
3066 following keyword arguments are supported:
3067 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003068
3069 Args:
3070 name: The name of the git command to call. Any '_' characters will
3071 be replaced with '-'.
3072
3073 Returns:
3074 A callable object that will try to call git with the named command.
3075 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003076 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003077
Dave Borowitz091f8932012-10-23 17:01:04 -07003078 def runner(*args, **kwargs):
3079 cmdv = []
3080 config = kwargs.pop('config', None)
3081 for k in kwargs:
3082 raise TypeError('%s() got an unexpected keyword argument %r'
3083 % (name, k))
3084 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303085 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003086 cmdv.append('-c')
3087 cmdv.append('%s=%s' % (k, v))
3088 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003089 cmdv.extend(args)
3090 p = GitCommand(self._project,
3091 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003092 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003093 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003094 capture_stdout=True,
3095 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003096 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003097 raise GitError('%s %s: %s' %
3098 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003099 r = p.stdout
3100 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3101 return r[:-1]
3102 return r
3103 return runner
3104
3105
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003107
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003108 def __str__(self):
3109 return 'prior sync failed; rebase still in progress'
3110
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003111
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003112class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003113
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003114 def __str__(self):
3115 return 'contains uncommitted changes'
3116
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003117
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003118class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003119
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003120 def __init__(self, project, text):
3121 self.project = project
3122 self.text = text
3123
3124 def Print(self, syncbuf):
3125 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3126 syncbuf.out.nl()
3127
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003128
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003129class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003130
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003131 def __init__(self, project, why):
3132 self.project = project
3133 self.why = why
3134
3135 def Print(self, syncbuf):
3136 syncbuf.out.fail('error: %s/: %s',
3137 self.project.relpath,
3138 str(self.why))
3139 syncbuf.out.nl()
3140
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003141
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003142class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003143
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003144 def __init__(self, project, action):
3145 self.project = project
3146 self.action = action
3147
3148 def Run(self, syncbuf):
3149 out = syncbuf.out
3150 out.project('project %s/', self.project.relpath)
3151 out.nl()
3152 try:
3153 self.action()
3154 out.nl()
3155 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003156 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003157 out.nl()
3158 return False
3159
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003160
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003161class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003162
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003163 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003164 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003165 self.project = self.printer('header', attr='bold')
3166 self.info = self.printer('info')
3167 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003168
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003169
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003170class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003171
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003172 def __init__(self, config, detach_head=False):
3173 self._messages = []
3174 self._failures = []
3175 self._later_queue1 = []
3176 self._later_queue2 = []
3177
3178 self.out = _SyncColoring(config)
3179 self.out.redirect(sys.stderr)
3180
3181 self.detach_head = detach_head
3182 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003183 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003184
3185 def info(self, project, fmt, *args):
3186 self._messages.append(_InfoMessage(project, fmt % args))
3187
3188 def fail(self, project, err=None):
3189 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003190 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003191
3192 def later1(self, project, what):
3193 self._later_queue1.append(_Later(project, what))
3194
3195 def later2(self, project, what):
3196 self._later_queue2.append(_Later(project, what))
3197
3198 def Finish(self):
3199 self._PrintMessages()
3200 self._RunLater()
3201 self._PrintMessages()
3202 return self.clean
3203
David Rileye0684ad2017-04-05 00:02:59 -07003204 def Recently(self):
3205 recent_clean = self.recent_clean
3206 self.recent_clean = True
3207 return recent_clean
3208
3209 def _MarkUnclean(self):
3210 self.clean = False
3211 self.recent_clean = False
3212
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003213 def _RunLater(self):
3214 for q in ['_later_queue1', '_later_queue2']:
3215 if not self._RunQueue(q):
3216 return
3217
3218 def _RunQueue(self, queue):
3219 for m in getattr(self, queue):
3220 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003221 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003222 return False
3223 setattr(self, queue, [])
3224 return True
3225
3226 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003227 if self._messages or self._failures:
3228 if os.isatty(2):
3229 self.out.write(progress.CSI_ERASE_LINE)
3230 self.out.write('\r')
3231
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003232 for m in self._messages:
3233 m.Print(self)
3234 for m in self._failures:
3235 m.Print(self)
3236
3237 self._messages = []
3238 self._failures = []
3239
3240
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003241class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003242
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003243 """A special project housed under .repo.
3244 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003245
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003246 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003247 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003248 manifest=manifest,
3249 name=name,
3250 gitdir=gitdir,
3251 objdir=gitdir,
3252 worktree=worktree,
3253 remote=RemoteSpec('origin'),
3254 relpath='.repo/%s' % name,
3255 revisionExpr='refs/heads/master',
3256 revisionId=None,
3257 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003258
3259 def PreSync(self):
3260 if self.Exists:
3261 cb = self.CurrentBranch
3262 if cb:
3263 base = self.GetBranch(cb).merge
3264 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003265 self.revisionExpr = base
3266 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003267
Martin Kelly224a31a2017-07-10 14:46:25 -07003268 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003269 """ Prepare MetaProject for manifest branch switch
3270 """
3271
3272 # detach and delete manifest branch, allowing a new
3273 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003274 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003275 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003276 syncbuf.Finish()
3277
3278 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003279 ['update-ref', '-d', 'refs/heads/default'],
3280 capture_stdout=True,
3281 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003282
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003283 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003284 def LastFetch(self):
3285 try:
3286 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3287 return os.path.getmtime(fh)
3288 except OSError:
3289 return 0
3290
3291 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003292 def HasChanges(self):
3293 """Has the remote received new commits not yet checked out?
3294 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003295 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003296 return False
3297
David Pursehouse8a68ff92012-09-24 12:15:13 +09003298 all_refs = self.bare_ref.all
3299 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003300 head = self.work_git.GetHead()
3301 if head.startswith(R_HEADS):
3302 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003303 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003304 except KeyError:
3305 head = None
3306
3307 if revid == head:
3308 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003309 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003310 return True
3311 return False