blob: de7bbbc9339d62ece374ce933d84e01162c8c07f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080015import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070016import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070017import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070018import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070019import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070020import re
21import shutil
22import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070023import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020025import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080026import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070027import time
Mike Frysingeracf63b22019-06-13 02:24:21 -040028import urllib.parse
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070029
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070030from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070031from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070032from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
33 ID_RE
Remy Bohmer16c13282020-09-10 10:38:04 +020034from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040035from error import ManifestInvalidRevisionError, ManifestInvalidPathError
Conley Owens75ee0572012-11-15 17:33:11 -080036from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070037import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040038import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040039from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M, R_WORKTREE_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070043
George Engelbrecht9bc283e2020-04-02 12:36:09 -060044# Maximum sleep time allowed during retries.
45MAXIMUM_RETRY_SLEEP_SEC = 3600.0
46# +-10% random jitter is added to each Fetches retry sleep duration.
47RETRY_JITTER_PERCENT = 0.1
48
49
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070050def _lwrite(path, content):
51 lock = '%s.lock' % path
52
Remy Bohmer169b0212020-11-21 10:57:52 +010053 # Maintain Unix line endings on all OS's to match git behavior.
54 with open(lock, 'w', newline='\n') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056
57 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070058 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080060 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 raise
62
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070063
Shawn O. Pearce48244782009-04-16 08:25:57 -070064def _error(fmt, *args):
65 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070066 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070067
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070068
David Pursehousef33929d2015-08-24 14:39:14 +090069def _warn(fmt, *args):
70 msg = fmt % args
71 print('warn: %s' % msg, file=sys.stderr)
72
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070073
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070074def not_rev(r):
75 return '^' + r
76
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080078def sq(r):
79 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080080
David Pursehouse819827a2020-02-12 15:20:19 +090081
Jonathan Nieder93719792015-03-17 11:29:58 -070082_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
84
Jonathan Nieder93719792015-03-17 11:29:58 -070085def _ProjectHooks():
86 """List the hooks present in the 'hooks' directory.
87
88 These hooks are project hooks and are copied to the '.git/hooks' directory
89 of all subprojects.
90
91 This function caches the list of hooks (based on the contents of the
92 'repo/hooks' directory) on the first call.
93
94 Returns:
95 A list of absolute paths to all of the files in the hooks directory.
96 """
97 global _project_hook_list
98 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -070099 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700100 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700101 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700102 return _project_hook_list
103
104
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700105class DownloadedChange(object):
106 _commit_cache = None
107
108 def __init__(self, project, base, change_id, ps_id, commit):
109 self.project = project
110 self.base = base
111 self.change_id = change_id
112 self.ps_id = ps_id
113 self.commit = commit
114
115 @property
116 def commits(self):
117 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700118 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
119 '--abbrev-commit',
120 '--pretty=oneline',
121 '--reverse',
122 '--date-order',
123 not_rev(self.base),
124 self.commit,
125 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700126 return self._commit_cache
127
128
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700129class ReviewableBranch(object):
130 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400131 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700132
133 def __init__(self, project, branch, base):
134 self.project = project
135 self.branch = branch
136 self.base = base
137
138 @property
139 def name(self):
140 return self.branch.name
141
142 @property
143 def commits(self):
144 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400145 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
146 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
147 try:
148 self._commit_cache = self.project.bare_git.rev_list(*args)
149 except GitError:
150 # We weren't able to probe the commits for this branch. Was it tracking
151 # a branch that no longer exists? If so, return no commits. Otherwise,
152 # rethrow the error as we don't know what's going on.
153 if self.base_exists:
154 raise
155
156 self._commit_cache = []
157
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mike Frysinger6da17752019-09-11 18:43:17 -0400176 @property
177 def base_exists(self):
178 """Whether the branch we're tracking exists.
179
180 Normally it should, but sometimes branches we track can get deleted.
181 """
182 if self._base_exists is None:
183 try:
184 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
185 # If we're still here, the base branch exists.
186 self._base_exists = True
187 except GitError:
188 # If we failed to verify, the base branch doesn't exist.
189 self._base_exists = False
190
191 return self._base_exists
192
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700193 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500194 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700195 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500196 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500197 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200198 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700199 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200200 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200201 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800202 validate_certs=True,
203 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500204 self.project.UploadForReview(branch=self.name,
205 people=people,
206 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700207 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500208 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500209 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200210 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700211 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200212 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200213 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800214 validate_certs=validate_certs,
215 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700217 def GetPublishedRefs(self):
218 refs = {}
219 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700220 self.branch.remote.SshReviewUrl(self.project.UserEmail),
221 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700222 for line in output.split('\n'):
223 try:
224 (sha, ref) = line.split()
225 refs[sha] = ref
226 except ValueError:
227 pass
228
229 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700234 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500235 super().__init__(config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100236 self.project = self.printer('header', attr='bold')
237 self.branch = self.printer('header', attr='bold')
238 self.nobranch = self.printer('nobranch', fg='red')
239 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Anthony King7bdac712014-07-16 12:56:40 +0100241 self.added = self.printer('added', fg='green')
242 self.changed = self.printer('changed', fg='red')
243 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244
245
246class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700247
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -0500249 super().__init__(config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100250 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400251 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700252
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700253
Anthony King7bdac712014-07-16 12:56:40 +0100254class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700255
James W. Mills24c13082012-04-12 15:04:13 -0500256 def __init__(self, name, value, keep):
257 self.name = name
258 self.value = value
259 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700261
Mike Frysingere6a202f2019-08-02 15:57:57 -0400262def _SafeExpandPath(base, subpath, skipfinal=False):
263 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Mike Frysingere6a202f2019-08-02 15:57:57 -0400265 We make sure no intermediate symlinks are traversed, and that the final path
266 is not a special file (e.g. not a socket or fifo).
267
268 NB: We rely on a number of paths already being filtered out while parsing the
269 manifest. See the validation logic in manifest_xml.py for more details.
270 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500271 # Split up the path by its components. We can't use os.path.sep exclusively
272 # as some platforms (like Windows) will convert / to \ and that bypasses all
273 # our constructed logic here. Especially since manifest authors only use
274 # / in their paths.
275 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
276 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400277 if skipfinal:
278 # Whether the caller handles the final component itself.
279 finalpart = components.pop()
280
281 path = base
282 for part in components:
283 if part in {'.', '..'}:
284 raise ManifestInvalidPathError(
285 '%s: "%s" not allowed in paths' % (subpath, part))
286
287 path = os.path.join(path, part)
288 if platform_utils.islink(path):
289 raise ManifestInvalidPathError(
290 '%s: traversing symlinks not allow' % (path,))
291
292 if os.path.exists(path):
293 if not os.path.isfile(path) and not platform_utils.isdir(path):
294 raise ManifestInvalidPathError(
295 '%s: only regular files & directories allowed' % (path,))
296
297 if skipfinal:
298 path = os.path.join(path, finalpart)
299
300 return path
301
302
303class _CopyFile(object):
304 """Container for <copyfile> manifest element."""
305
306 def __init__(self, git_worktree, src, topdir, dest):
307 """Register a <copyfile> request.
308
309 Args:
310 git_worktree: Absolute path to the git project checkout.
311 src: Relative path under |git_worktree| of file to read.
312 topdir: Absolute path to the top of the repo client checkout.
313 dest: Relative path under |topdir| of file to write.
314 """
315 self.git_worktree = git_worktree
316 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700317 self.src = src
318 self.dest = dest
319
320 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400321 src = _SafeExpandPath(self.git_worktree, self.src)
322 dest = _SafeExpandPath(self.topdir, self.dest)
323
324 if platform_utils.isdir(src):
325 raise ManifestInvalidPathError(
326 '%s: copying from directory not supported' % (self.src,))
327 if platform_utils.isdir(dest):
328 raise ManifestInvalidPathError(
329 '%s: copying to directory not allowed' % (self.dest,))
330
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700331 # copy file if it does not exist or is out of date
332 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
333 try:
334 # remove existing file first, since it might be read-only
335 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800336 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400337 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200338 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700339 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200340 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700341 shutil.copy(src, dest)
342 # make the file read-only
343 mode = os.stat(dest)[stat.ST_MODE]
344 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
345 os.chmod(dest, mode)
346 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700347 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Anthony King7bdac712014-07-16 12:56:40 +0100350class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400351 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700352
Mike Frysingere6a202f2019-08-02 15:57:57 -0400353 def __init__(self, git_worktree, src, topdir, dest):
354 """Register a <linkfile> request.
355
356 Args:
357 git_worktree: Absolute path to the git project checkout.
358 src: Target of symlink relative to path under |git_worktree|.
359 topdir: Absolute path to the top of the repo client checkout.
360 dest: Relative path under |topdir| of symlink to create.
361 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700362 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400363 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500364 self.src = src
365 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500366
Wink Saville4c426ef2015-06-03 08:05:17 -0700367 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500368 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700369 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500370 try:
371 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800372 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800373 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500374 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700375 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700376 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500377 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700378 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500379 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700380 _error('Cannot link file %s to %s', relSrc, absDest)
381
382 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400383 """Link the self.src & self.dest paths.
384
385 Handles wild cards on the src linking all of the files in the source in to
386 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700387 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500388 # Some people use src="." to create stable links to projects. Lets allow
389 # that but reject all other uses of "." to keep things simple.
390 if self.src == '.':
391 src = self.git_worktree
392 else:
393 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400394
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300395 if not glob.has_magic(src):
396 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400397 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
398 # dest & src are absolute paths at this point. Make sure the target of
399 # the symlink is relative in the context of the repo client checkout.
400 relpath = os.path.relpath(src, os.path.dirname(dest))
401 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700402 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400403 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300404 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400405 if os.path.exists(dest) and not platform_utils.isdir(dest):
406 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700407 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400408 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700409 # Create a releative path from source dir to destination dir
410 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400411 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700412
413 # Get the source file name
414 srcFile = os.path.basename(absSrcFile)
415
416 # Now form the final full paths to srcFile. They will be
417 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400418 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700419 relSrc = os.path.join(relSrcDir, srcFile)
420 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500421
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700422
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700423class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700424
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700425 def __init__(self,
426 name,
Anthony King7bdac712014-07-16 12:56:40 +0100427 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700428 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100429 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700430 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700431 orig_name=None,
432 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700433 self.name = name
434 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700435 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700436 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100437 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700438 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700439 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700440
Ian Kasprzak0286e312021-02-05 10:06:18 -0800441
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700442class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600443 # These objects can be shared between several working trees.
444 shareable_files = ['description', 'info']
445 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
446 # These objects can only be used by a single working tree.
447 working_tree_files = ['config', 'packed-refs', 'shallow']
448 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700449
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700450 def __init__(self,
451 manifest,
452 name,
453 remote,
454 gitdir,
David James8d201162013-10-11 17:03:19 -0700455 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700456 worktree,
457 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700458 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800459 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100460 rebase=True,
461 groups=None,
462 sync_c=False,
463 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900464 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100465 clone_depth=None,
466 upstream=None,
467 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500468 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100469 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900470 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700471 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600472 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700473 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800474 """Init a Project object.
475
476 Args:
477 manifest: The XmlManifest object.
478 name: The `name` attribute of manifest.xml's project element.
479 remote: RemoteSpec object specifying its remote's properties.
480 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700481 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800482 worktree: Absolute path of git working tree.
483 relpath: Relative path of git working tree to repo's top directory.
484 revisionExpr: The `revision` attribute of manifest.xml's project element.
485 revisionId: git commit id for checking out.
486 rebase: The `rebase` attribute of manifest.xml's project element.
487 groups: The `groups` attribute of manifest.xml's project element.
488 sync_c: The `sync-c` attribute of manifest.xml's project element.
489 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900490 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800491 upstream: The `upstream` attribute of manifest.xml's project element.
492 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500493 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800494 is_derived: False if the project was explicitly defined in the manifest;
495 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400496 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900497 optimized_fetch: If True, when a project is set to a sha1 revision, only
498 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600499 retry_fetches: Retry remote fetches n times upon receiving transient error
500 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700501 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800502 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400503 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700504 self.name = name
505 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800506 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700507 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800508 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700509 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800510 else:
511 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700512 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700513 self.revisionExpr = revisionExpr
514
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700515 if revisionId is None \
516 and revisionExpr \
517 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700518 self.revisionId = revisionExpr
519 else:
520 self.revisionId = revisionId
521
Mike Pontillod3153822012-02-28 11:53:24 -0800522 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700523 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700524 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800525 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900526 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900527 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700528 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800529 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500530 # NB: Do not use this setting in __init__ to change behavior so that the
531 # manifest.git checkout can inspect & change it after instantiating. See
532 # the XmlManifest init code for more info.
533 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800534 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900535 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600536 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800537 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800538
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700539 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700540 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500541 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500542 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700543 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400544 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700545
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800546 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700547 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800548 else:
549 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700550 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700551 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700552 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400553 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700554 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555
Doug Anderson37282b42011-03-04 11:54:18 -0800556 # This will be filled in if a project is later identified to be the
557 # project containing repo hooks.
558 self.enabled_repo_hooks = []
559
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700560 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800561 def Derived(self):
562 return self.is_derived
563
564 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700566 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700567
568 @property
569 def CurrentBranch(self):
570 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400571
572 The branch name omits the 'refs/heads/' prefix.
573 None is returned if the project is on a detached HEAD, or if the work_git is
574 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400576 try:
577 b = self.work_git.GetHead()
578 except NoManifestException:
579 # If the local checkout is in a bad state, don't barf. Let the callers
580 # process this like the head is unreadable.
581 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700582 if b.startswith(R_HEADS):
583 return b[len(R_HEADS):]
584 return None
585
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700586 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500587 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
588 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
589 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200590
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700591 def IsDirty(self, consider_untracked=True):
592 """Is the working directory modified in some way?
593 """
594 self.work_git.update_index('-q',
595 '--unmerged',
596 '--ignore-missing',
597 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900598 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700599 return True
600 if self.work_git.DiffZ('diff-files'):
601 return True
602 if consider_untracked and self.work_git.LsOthers():
603 return True
604 return False
605
606 _userident_name = None
607 _userident_email = None
608
609 @property
610 def UserName(self):
611 """Obtain the user's personal name.
612 """
613 if self._userident_name is None:
614 self._LoadUserIdentity()
615 return self._userident_name
616
617 @property
618 def UserEmail(self):
619 """Obtain the user's email address. This is very likely
620 to be their Gerrit login.
621 """
622 if self._userident_email is None:
623 self._LoadUserIdentity()
624 return self._userident_email
625
626 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900627 u = self.bare_git.var('GIT_COMMITTER_IDENT')
628 m = re.compile("^(.*) <([^>]*)> ").match(u)
629 if m:
630 self._userident_name = m.group(1)
631 self._userident_email = m.group(2)
632 else:
633 self._userident_name = ''
634 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700635
636 def GetRemote(self, name):
637 """Get the configuration for a single remote.
638 """
639 return self.config.GetRemote(name)
640
641 def GetBranch(self, name):
642 """Get the configuration for a single branch.
643 """
644 return self.config.GetBranch(name)
645
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700646 def GetBranches(self):
647 """Get all existing local branches.
648 """
649 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900650 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700651 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700652
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530653 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700654 if name.startswith(R_HEADS):
655 name = name[len(R_HEADS):]
656 b = self.GetBranch(name)
657 b.current = name == current
658 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900659 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700660 heads[name] = b
661
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530662 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700663 if name.startswith(R_PUB):
664 name = name[len(R_PUB):]
665 b = heads.get(name)
666 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900667 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700668
669 return heads
670
Colin Cross5acde752012-03-28 20:15:45 -0700671 def MatchesGroups(self, manifest_groups):
672 """Returns true if the manifest groups specified at init should cause
673 this project to be synced.
674 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700675 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700676
677 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700678 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700679 manifest_groups: "-group1,group2"
680 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500681
682 The special manifest group "default" will match any project that
683 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700684 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500685 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700686 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700687 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500688 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700689
Conley Owens971de8e2012-04-16 10:36:08 -0700690 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700691 for group in expanded_manifest_groups:
692 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700693 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700694 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700695 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700696
Conley Owens971de8e2012-04-16 10:36:08 -0700697 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700698
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700699# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700700 def UncommitedFiles(self, get_all=True):
701 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700702
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700703 Args:
704 get_all: a boolean, if True - get information about all different
705 uncommitted files. If False - return as soon as any kind of
706 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500707 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700708 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500709 self.work_git.update_index('-q',
710 '--unmerged',
711 '--ignore-missing',
712 '--refresh')
713 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700714 details.append("rebase in progress")
715 if not get_all:
716 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500717
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700718 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
719 if changes:
720 details.extend(changes)
721 if not get_all:
722 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500723
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 changes = self.work_git.DiffZ('diff-files').keys()
725 if changes:
726 details.extend(changes)
727 if not get_all:
728 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500729
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700730 changes = self.work_git.LsOthers()
731 if changes:
732 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500733
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 return details
735
736 def HasChanges(self):
737 """Returns true if there are uncommitted changes.
738 """
739 if self.UncommitedFiles(get_all=False):
740 return True
741 else:
742 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500743
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600744 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700745 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200746
747 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200748 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600749 quiet: If True then only print the project name. Do not print
750 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700752 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700753 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200754 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700755 print(file=output_redir)
756 print('project %s/' % self.relpath, file=output_redir)
757 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700758 return
759
760 self.work_git.update_index('-q',
761 '--unmerged',
762 '--ignore-missing',
763 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700764 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
766 df = self.work_git.DiffZ('diff-files')
767 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100768 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700769 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700770
771 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700772 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200773 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700774 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600776 if quiet:
777 out.nl()
778 return 'DIRTY'
779
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 branch = self.CurrentBranch
781 if branch is None:
782 out.nobranch('(*** NO BRANCH ***)')
783 else:
784 out.branch('branch %s', branch)
785 out.nl()
786
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700787 if rb:
788 out.important('prior sync failed; rebase still in progress')
789 out.nl()
790
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700791 paths = list()
792 paths.extend(di.keys())
793 paths.extend(df.keys())
794 paths.extend(do)
795
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530796 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900797 try:
798 i = di[p]
799 except KeyError:
800 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900802 try:
803 f = df[p]
804 except KeyError:
805 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200806
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900807 if i:
808 i_status = i.status.upper()
809 else:
810 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900812 if f:
813 f_status = f.status.lower()
814 else:
815 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700816
817 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800818 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700819 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700820 else:
821 line = ' %s%s\t%s' % (i_status, f_status, p)
822
823 if i and not f:
824 out.added('%s', line)
825 elif (i and f) or (not i and f):
826 out.changed('%s', line)
827 elif not i and not f:
828 out.untracked('%s', line)
829 else:
830 out.write('%s', line)
831 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200832
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700833 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700834
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500835 def PrintWorkTreeDiff(self, absolute_paths=False, output_redir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 """Prints the status of the repository to stdout.
837 """
838 out = DiffColoring(self.config)
Mike Frysinger69b4a9c2021-02-16 18:18:01 -0500839 if output_redir:
840 out.redirect(output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700841 cmd = ['diff']
842 if out.is_on:
843 cmd.append('--color')
844 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300845 if absolute_paths:
846 cmd.append('--src-prefix=a/%s/' % self.relpath)
847 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700848 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400849 try:
850 p = GitCommand(self,
851 cmd,
852 capture_stdout=True,
853 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -0500854 p.Wait()
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400855 except GitError as e:
856 out.nl()
857 out.project('project %s/' % self.relpath)
858 out.nl()
859 out.fail('%s', str(e))
860 out.nl()
861 return False
Mike Frysinger84230002021-02-16 17:08:35 -0500862 if p.stdout:
863 out.nl()
864 out.project('project %s/' % self.relpath)
865 out.nl()
866 out.write(p.stdout)
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400867 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700869# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900870 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700871 """Was the branch published (uploaded) for code review?
872 If so, returns the SHA-1 hash of the last published
873 state for the branch.
874 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700875 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900876 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700877 try:
878 return self.bare_git.rev_parse(key)
879 except GitError:
880 return None
881 else:
882 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900883 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700884 except KeyError:
885 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886
David Pursehouse8a68ff92012-09-24 12:15:13 +0900887 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700888 """Prunes any stale published refs.
889 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900890 if all_refs is None:
891 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700892 heads = set()
893 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530894 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700895 if name.startswith(R_HEADS):
896 heads.add(name)
897 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900898 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700899
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530900 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700901 n = name[len(R_PUB):]
902 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900903 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700905 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700906 """List any branches which can be uploaded for review.
907 """
908 heads = {}
909 pubed = {}
910
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530911 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700912 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900913 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700914 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900915 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916
917 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530918 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900919 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700920 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700921 if selected_branch and branch != selected_branch:
922 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700923
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800924 rb = self.GetUploadableBranch(branch)
925 if rb:
926 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 return ready
928
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800929 def GetUploadableBranch(self, branch_name):
930 """Get a single uploadable branch, or None.
931 """
932 branch = self.GetBranch(branch_name)
933 base = branch.LocalMerge
934 if branch.LocalMerge:
935 rb = ReviewableBranch(self, branch, base)
936 if rb.commits:
937 return rb
938 return None
939
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700940 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100941 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500942 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700943 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500944 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500945 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200946 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700947 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200948 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200949 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800950 validate_certs=True,
951 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952 """Uploads the named branch for code review.
953 """
954 if branch is None:
955 branch = self.CurrentBranch
956 if branch is None:
957 raise GitError('not currently on a branch')
958
959 branch = self.GetBranch(branch)
960 if not branch.LocalMerge:
961 raise GitError('branch %s does not track a remote' % branch.name)
962 if not branch.remote.review:
963 raise GitError('remote %s has no review url' % branch.remote.name)
964
Bryan Jacobsf609f912013-05-06 13:36:24 -0400965 if dest_branch is None:
966 dest_branch = self.dest_branch
967 if dest_branch is None:
968 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700969 if not dest_branch.startswith(R_HEADS):
970 dest_branch = R_HEADS + dest_branch
971
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800972 if not branch.remote.projectname:
973 branch.remote.projectname = self.name
974 branch.remote.Save()
975
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200976 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800977 if url is None:
978 raise UploadError('review not configured')
979 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500980 if dryrun:
981 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800982
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800983 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -0800984 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700985
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800986 for push_option in (push_options or []):
987 cmd.append('-o')
988 cmd.append(push_option)
989
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800990 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800991
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800992 if dest_branch.startswith(R_HEADS):
993 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -0700994
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -0500995 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -0800996 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800997 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -0800998 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -0500999 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001000 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001001
David Pursehousef25a3702018-11-14 19:01:22 -08001002 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001003 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001004 if notify:
1005 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001006 if private:
1007 opts += ['private']
1008 if wip:
1009 opts += ['wip']
1010 if opts:
1011 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001012 cmd.append(ref_spec)
1013
Anthony King7bdac712014-07-16 12:56:40 +01001014 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001015 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016
Mike Frysingerd7f86832020-11-19 19:18:46 -05001017 if not dryrun:
1018 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1019 self.bare_git.UpdateRef(R_PUB + branch.name,
1020 R_HEADS + branch.name,
1021 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001022
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001023# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001024 def _ExtractArchive(self, tarpath, path=None):
1025 """Extract the given tar on its current location
1026
1027 Args:
1028 - tarpath: The path to the actual tar file
1029
1030 """
1031 try:
1032 with tarfile.open(tarpath, 'r') as tar:
1033 tar.extractall(path=path)
1034 return True
1035 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001036 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001037 return False
1038
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001039 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001040 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001041 verbose=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001042 is_new=None,
1043 current_branch_only=False,
1044 force_sync=False,
1045 clone_bundle=True,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001046 tags=True,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001047 archive=False,
1048 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001049 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001050 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001051 submodules=False,
1052 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001053 """Perform only the network IO portion of the sync process.
1054 Local working directory/branch state is not affected.
1055 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001056 if archive and not isinstance(self, MetaProject):
1057 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001058 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001059 return False
1060
1061 name = self.relpath.replace('\\', '/')
1062 name = name.replace('/', '_')
1063 tarpath = '%s.tar' % name
1064 topdir = self.manifest.topdir
1065
1066 try:
1067 self._FetchArchive(tarpath, cwd=topdir)
1068 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001069 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001070 return False
1071
1072 # From now on, we only need absolute tarpath
1073 tarpath = os.path.join(topdir, tarpath)
1074
1075 if not self._ExtractArchive(tarpath, path=topdir):
1076 return False
1077 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001078 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001079 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001080 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001081 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001082 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001083 if is_new is None:
1084 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001085 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001086 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001087 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001088 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001089 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001090
1091 if is_new:
1092 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1093 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001094 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001095 # This works for both absolute and relative alternate directories.
1096 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001097 except IOError:
1098 alt_dir = None
1099 else:
1100 alt_dir = None
1101
Mike Frysingere50b6a72020-02-19 01:45:48 -05001102 if (clone_bundle
1103 and alt_dir is None
1104 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001105 is_new = False
1106
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001107 if not current_branch_only:
1108 if self.sync_c:
1109 current_branch_only = True
1110 elif not self.manifest._loaded:
1111 # Manifest cannot check defaults until it syncs.
1112 current_branch_only = False
1113 elif self.manifest.default.sync_c:
1114 current_branch_only = True
1115
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001116 if not self.sync_tags:
1117 tags = False
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001118
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001119 if self.clone_depth:
1120 depth = self.clone_depth
1121 else:
1122 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1123
Mike Frysinger521d01b2020-02-17 01:51:49 -05001124 # See if we can skip the network fetch entirely.
1125 if not (optimized_fetch and
1126 (ID_RE.match(self.revisionExpr) and
1127 self._CheckForImmutableRevision())):
1128 if not self._RemoteFetch(
David Pursehouse3cceda52020-02-18 14:11:39 +09001129 initial=is_new, quiet=quiet, verbose=verbose, alt_dir=alt_dir,
1130 current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001131 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001132 submodules=submodules, force_sync=force_sync,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001133 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001134 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001135
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001136 mp = self.manifest.manifestProject
1137 dissociate = mp.config.GetBoolean('repo.dissociate')
1138 if dissociate:
1139 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1140 if os.path.exists(alternates_file):
1141 cmd = ['repack', '-a', '-d']
1142 if GitCommand(self, cmd, bare=True).Wait() != 0:
1143 return False
1144 platform_utils.remove(alternates_file)
1145
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001146 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001147 self._InitMRef()
1148 else:
1149 self._InitMirrorHead()
1150 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001151 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001152 except OSError:
1153 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001154 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001155
1156 def PostRepoUpgrade(self):
1157 self._InitHooks()
1158
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001159 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001160 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001161 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001162 for copyfile in self.copyfiles:
1163 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001164 for linkfile in self.linkfiles:
1165 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001166
Julien Camperguedd654222014-01-09 16:21:37 +01001167 def GetCommitRevisionId(self):
1168 """Get revisionId of a commit.
1169
1170 Use this method instead of GetRevisionId to get the id of the commit rather
1171 than the id of the current git object (for example, a tag)
1172
1173 """
1174 if not self.revisionExpr.startswith(R_TAGS):
1175 return self.GetRevisionId(self._allrefs)
1176
1177 try:
1178 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1179 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001180 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1181 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001182
David Pursehouse8a68ff92012-09-24 12:15:13 +09001183 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001184 if self.revisionId:
1185 return self.revisionId
1186
1187 rem = self.GetRemote(self.remote.name)
1188 rev = rem.ToLocal(self.revisionExpr)
1189
David Pursehouse8a68ff92012-09-24 12:15:13 +09001190 if all_refs is not None and rev in all_refs:
1191 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001192
1193 try:
1194 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1195 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001196 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1197 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001198
Raman Tenneti6a872c92021-01-14 19:17:50 -08001199 def SetRevisionId(self, revisionId):
1200 self.revisionId = revisionId
1201
Martin Kellye4e94d22017-03-21 16:05:12 -07001202 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203 """Perform only the local IO portion of the sync process.
1204 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001205 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001206 if not os.path.exists(self.gitdir):
1207 syncbuf.fail(self,
1208 'Cannot checkout %s due to missing network sync; Run '
1209 '`repo sync -n %s` first.' %
1210 (self.name, self.name))
1211 return
1212
Martin Kellye4e94d22017-03-21 16:05:12 -07001213 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001214 all_refs = self.bare_ref.all
1215 self.CleanPublishedCache(all_refs)
1216 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001217
David Pursehouse1d947b32012-10-25 12:23:11 +09001218 def _doff():
1219 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001220 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001221
Martin Kellye4e94d22017-03-21 16:05:12 -07001222 def _dosubmodules():
1223 self._SyncSubmodules(quiet=True)
1224
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001225 head = self.work_git.GetHead()
1226 if head.startswith(R_HEADS):
1227 branch = head[len(R_HEADS):]
1228 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001229 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001230 except KeyError:
1231 head = None
1232 else:
1233 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001234
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001235 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001236 # Currently on a detached HEAD. The user is assumed to
1237 # not have any local modifications worth worrying about.
1238 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001239 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001240 syncbuf.fail(self, _PriorSyncFailedError())
1241 return
1242
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001243 if head == revid:
1244 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001245 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001246 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001247 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001248 # The copy/linkfile config may have changed.
1249 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001250 return
1251 else:
1252 lost = self._revlist(not_rev(revid), HEAD)
1253 if lost:
1254 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001255
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001256 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001257 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001258 if submodules:
1259 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001260 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001261 syncbuf.fail(self, e)
1262 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001263 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001264 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001265
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001266 if head == revid:
1267 # No changes; don't do anything further.
1268 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001269 # The copy/linkfile config may have changed.
1270 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001271 return
1272
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001273 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001274
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001275 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001276 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001277 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001278 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001279 syncbuf.info(self,
1280 "leaving %s; does not track upstream",
1281 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001282 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001283 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001284 if submodules:
1285 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001286 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001287 syncbuf.fail(self, e)
1288 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001289 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001290 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001292 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001293
1294 # See if we can perform a fast forward merge. This can happen if our
1295 # branch isn't in the exact same state as we last published.
1296 try:
1297 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1298 # Skip the published logic.
1299 pub = False
1300 except GitError:
1301 pub = self.WasPublished(branch.name, all_refs)
1302
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001303 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001304 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001305 if not_merged:
1306 if upstream_gain:
1307 # The user has published this branch and some of those
1308 # commits are not yet merged upstream. We do not want
1309 # to rewrite the published commits so we punt.
1310 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001311 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001312 "branch %s is published (but not merged) and is now "
1313 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001314 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001315 elif pub == head:
1316 # All published commits are merged, and thus we are a
1317 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001318 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001319 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001320 if submodules:
1321 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001322 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001323
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001324 # Examine the local commits not in the remote. Find the
1325 # last one attributed to this user, if any.
1326 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001327 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001328 last_mine = None
1329 cnt_mine = 0
1330 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001331 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001332 if committer_email == self.UserEmail:
1333 last_mine = commit_id
1334 cnt_mine += 1
1335
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001336 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001337 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001338
1339 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001340 syncbuf.fail(self, _DirtyError())
1341 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001342
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001343 # If the upstream switched on us, warn the user.
1344 #
1345 if branch.merge != self.revisionExpr:
1346 if branch.merge and self.revisionExpr:
1347 syncbuf.info(self,
1348 'manifest switched %s...%s',
1349 branch.merge,
1350 self.revisionExpr)
1351 elif branch.merge:
1352 syncbuf.info(self,
1353 'manifest no longer tracks %s',
1354 branch.merge)
1355
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001356 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001357 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001358 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001359 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001360 syncbuf.info(self,
1361 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001362 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001363
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001364 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001365 if not ID_RE.match(self.revisionExpr):
1366 # in case of manifest sync the revisionExpr might be a SHA1
1367 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001368 if not branch.merge.startswith('refs/'):
1369 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001370 branch.Save()
1371
Mike Pontillod3153822012-02-28 11:53:24 -08001372 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001373 def _docopyandlink():
1374 self._CopyAndLinkFiles()
1375
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001376 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001377 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001378 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001379 if submodules:
1380 syncbuf.later2(self, _dosubmodules)
1381 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001382 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001383 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001384 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001385 if submodules:
1386 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001387 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001388 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001389 syncbuf.fail(self, e)
1390 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001391 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001392 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001393 if submodules:
1394 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001395
Mike Frysingere6a202f2019-08-02 15:57:57 -04001396 def AddCopyFile(self, src, dest, topdir):
1397 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001398
Mike Frysingere6a202f2019-08-02 15:57:57 -04001399 No filesystem changes occur here. Actual copying happens later on.
1400
1401 Paths should have basic validation run on them before being queued.
1402 Further checking will be handled when the actual copy happens.
1403 """
1404 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1405
1406 def AddLinkFile(self, src, dest, topdir):
1407 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1408
1409 No filesystem changes occur here. Actual linking happens later on.
1410
1411 Paths should have basic validation run on them before being queued.
1412 Further checking will be handled when the actual link happens.
1413 """
1414 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001415
James W. Mills24c13082012-04-12 15:04:13 -05001416 def AddAnnotation(self, name, value, keep):
1417 self.annotations.append(_Annotation(name, value, keep))
1418
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001419 def DownloadPatchSet(self, change_id, patch_id):
1420 """Download a single patch set of a single change to FETCH_HEAD.
1421 """
1422 remote = self.GetRemote(self.remote.name)
1423
1424 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001425 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001426 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001427 if GitCommand(self, cmd, bare=True).Wait() != 0:
1428 return None
1429 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001430 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001431 change_id,
1432 patch_id,
1433 self.bare_git.rev_parse('FETCH_HEAD'))
1434
Mike Frysingerc0d18662020-02-19 19:19:18 -05001435 def DeleteWorktree(self, quiet=False, force=False):
1436 """Delete the source checkout and any other housekeeping tasks.
1437
1438 This currently leaves behind the internal .repo/ cache state. This helps
1439 when switching branches or manifest changes get reverted as we don't have
1440 to redownload all the git objects. But we should do some GC at some point.
1441
1442 Args:
1443 quiet: Whether to hide normal messages.
1444 force: Always delete tree even if dirty.
1445
1446 Returns:
1447 True if the worktree was completely cleaned out.
1448 """
1449 if self.IsDirty():
1450 if force:
1451 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1452 (self.relpath,), file=sys.stderr)
1453 else:
1454 print('error: %s: Cannot remove project: uncommitted changes are '
1455 'present.\n' % (self.relpath,), file=sys.stderr)
1456 return False
1457
1458 if not quiet:
1459 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1460
1461 # Unlock and delink from the main worktree. We don't use git's worktree
1462 # remove because it will recursively delete projects -- we handle that
1463 # ourselves below. https://crbug.com/git/48
1464 if self.use_git_worktrees:
1465 needle = platform_utils.realpath(self.gitdir)
1466 # Find the git worktree commondir under .repo/worktrees/.
1467 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1468 assert output.startswith('worktree '), output
1469 commondir = output[9:]
1470 # Walk each of the git worktrees to see where they point.
1471 configs = os.path.join(commondir, 'worktrees')
1472 for name in os.listdir(configs):
1473 gitdir = os.path.join(configs, name, 'gitdir')
1474 with open(gitdir) as fp:
1475 relpath = fp.read().strip()
1476 # Resolve the checkout path and see if it matches this project.
1477 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1478 if fullpath == needle:
1479 platform_utils.rmtree(os.path.join(configs, name))
1480
1481 # Delete the .git directory first, so we're less likely to have a partially
1482 # working git repository around. There shouldn't be any git projects here,
1483 # so rmtree works.
1484
1485 # Try to remove plain files first in case of git worktrees. If this fails
1486 # for any reason, we'll fall back to rmtree, and that'll display errors if
1487 # it can't remove things either.
1488 try:
1489 platform_utils.remove(self.gitdir)
1490 except OSError:
1491 pass
1492 try:
1493 platform_utils.rmtree(self.gitdir)
1494 except OSError as e:
1495 if e.errno != errno.ENOENT:
1496 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1497 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1498 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1499 return False
1500
1501 # Delete everything under the worktree, except for directories that contain
1502 # another git project.
1503 dirs_to_remove = []
1504 failed = False
1505 for root, dirs, files in platform_utils.walk(self.worktree):
1506 for f in files:
1507 path = os.path.join(root, f)
1508 try:
1509 platform_utils.remove(path)
1510 except OSError as e:
1511 if e.errno != errno.ENOENT:
1512 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1513 failed = True
1514 dirs[:] = [d for d in dirs
1515 if not os.path.lexists(os.path.join(root, d, '.git'))]
1516 dirs_to_remove += [os.path.join(root, d) for d in dirs
1517 if os.path.join(root, d) not in dirs_to_remove]
1518 for d in reversed(dirs_to_remove):
1519 if platform_utils.islink(d):
1520 try:
1521 platform_utils.remove(d)
1522 except OSError as e:
1523 if e.errno != errno.ENOENT:
1524 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1525 failed = True
1526 elif not platform_utils.listdir(d):
1527 try:
1528 platform_utils.rmdir(d)
1529 except OSError as e:
1530 if e.errno != errno.ENOENT:
1531 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1532 failed = True
1533 if failed:
1534 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1535 file=sys.stderr)
1536 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1537 return False
1538
1539 # Try deleting parent dirs if they are empty.
1540 path = self.worktree
1541 while path != self.manifest.topdir:
1542 try:
1543 platform_utils.rmdir(path)
1544 except OSError as e:
1545 if e.errno != errno.ENOENT:
1546 break
1547 path = os.path.dirname(path)
1548
1549 return True
1550
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001551# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001552 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553 """Create a new branch off the manifest's revision.
1554 """
Simran Basib9a1b732015-08-20 12:19:28 -07001555 if not branch_merge:
1556 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001557 head = self.work_git.GetHead()
1558 if head == (R_HEADS + name):
1559 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001560
David Pursehouse8a68ff92012-09-24 12:15:13 +09001561 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001562 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001563 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001564 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001565 capture_stdout=True,
1566 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001567
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001568 branch = self.GetBranch(name)
1569 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001570 branch.merge = branch_merge
1571 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1572 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001573
1574 if revision is None:
1575 revid = self.GetRevisionId(all_refs)
1576 else:
1577 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001578
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001579 if head.startswith(R_HEADS):
1580 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001581 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001582 except KeyError:
1583 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001584 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001585 ref = R_HEADS + name
1586 self.work_git.update_ref(ref, revid)
1587 self.work_git.symbolic_ref(HEAD, ref)
1588 branch.Save()
1589 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001590
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001591 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001592 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001593 capture_stdout=True,
1594 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001595 branch.Save()
1596 return True
1597 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001598
Wink Saville02d79452009-04-10 13:01:24 -07001599 def CheckoutBranch(self, name):
1600 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001601
1602 Args:
1603 name: The name of the branch to checkout.
1604
1605 Returns:
1606 True if the checkout succeeded; False if it didn't; None if the branch
1607 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001608 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001609 rev = R_HEADS + name
1610 head = self.work_git.GetHead()
1611 if head == rev:
1612 # Already on the branch
1613 #
1614 return True
Wink Saville02d79452009-04-10 13:01:24 -07001615
David Pursehouse8a68ff92012-09-24 12:15:13 +09001616 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001617 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001618 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001619 except KeyError:
1620 # Branch does not exist in this project
1621 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001622 return None
Wink Saville02d79452009-04-10 13:01:24 -07001623
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001624 if head.startswith(R_HEADS):
1625 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001626 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001627 except KeyError:
1628 head = None
1629
1630 if head == revid:
1631 # Same revision; just update HEAD to point to the new
1632 # target branch, but otherwise take no other action.
1633 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001634 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1635 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001636 return True
1637
1638 return GitCommand(self,
1639 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001640 capture_stdout=True,
1641 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001642
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001643 def AbandonBranch(self, name):
1644 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001645
1646 Args:
1647 name: The name of the branch to abandon.
1648
1649 Returns:
1650 True if the abandon succeeded; False if it didn't; None if the branch
1651 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001652 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001653 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001654 all_refs = self.bare_ref.all
1655 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001656 # Doesn't exist
1657 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001658
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001659 head = self.work_git.GetHead()
1660 if head == rev:
1661 # We can't destroy the branch while we are sitting
1662 # on it. Switch to a detached HEAD.
1663 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001664 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001665
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001667 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001668 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001669 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001670 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001671
1672 return GitCommand(self,
1673 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001674 capture_stdout=True,
1675 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001676
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001677 def PruneHeads(self):
1678 """Prune any topic branches already merged into upstream.
1679 """
1680 cb = self.CurrentBranch
1681 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001682 left = self._allrefs
1683 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001684 if name.startswith(R_HEADS):
1685 name = name[len(R_HEADS):]
1686 if cb is None or name != cb:
1687 kill.append(name)
1688
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001689 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001690 if cb is not None \
1691 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001692 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001693 self.work_git.DetachHead(HEAD)
1694 kill.append(cb)
1695
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001696 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001697 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001698
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001699 try:
1700 self.bare_git.DetachHead(rev)
1701
1702 b = ['branch', '-d']
1703 b.extend(kill)
1704 b = GitCommand(self, b, bare=True,
1705 capture_stdout=True,
1706 capture_stderr=True)
1707 b.Wait()
1708 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001709 if ID_RE.match(old):
1710 self.bare_git.DetachHead(old)
1711 else:
1712 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001713 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001715 for branch in kill:
1716 if (R_HEADS + branch) not in left:
1717 self.CleanPublishedCache()
1718 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001719
1720 if cb and cb not in kill:
1721 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001722 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723
1724 kept = []
1725 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001726 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727 branch = self.GetBranch(branch)
1728 base = branch.LocalMerge
1729 if not base:
1730 base = rev
1731 kept.append(ReviewableBranch(self, branch, base))
1732 return kept
1733
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001734# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001735 def GetRegisteredSubprojects(self):
1736 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001737
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001738 def rec(subprojects):
1739 if not subprojects:
1740 return
1741 result.extend(subprojects)
1742 for p in subprojects:
1743 rec(p.subprojects)
1744 rec(self.subprojects)
1745 return result
1746
1747 def _GetSubmodules(self):
1748 # Unfortunately we cannot call `git submodule status --recursive` here
1749 # because the working tree might not exist yet, and it cannot be used
1750 # without a working tree in its current implementation.
1751
1752 def get_submodules(gitdir, rev):
1753 # Parse .gitmodules for submodule sub_paths and sub_urls
1754 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1755 if not sub_paths:
1756 return []
1757 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1758 # revision of submodule repository
1759 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1760 submodules = []
1761 for sub_path, sub_url in zip(sub_paths, sub_urls):
1762 try:
1763 sub_rev = sub_revs[sub_path]
1764 except KeyError:
1765 # Ignore non-exist submodules
1766 continue
1767 submodules.append((sub_rev, sub_path, sub_url))
1768 return submodules
1769
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001770 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1771 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001772
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001773 def parse_gitmodules(gitdir, rev):
1774 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1775 try:
Anthony King7bdac712014-07-16 12:56:40 +01001776 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1777 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001778 except GitError:
1779 return [], []
1780 if p.Wait() != 0:
1781 return [], []
1782
1783 gitmodules_lines = []
1784 fd, temp_gitmodules_path = tempfile.mkstemp()
1785 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001786 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001787 os.close(fd)
1788 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001789 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1790 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001791 if p.Wait() != 0:
1792 return [], []
1793 gitmodules_lines = p.stdout.split('\n')
1794 except GitError:
1795 return [], []
1796 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001797 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001798
1799 names = set()
1800 paths = {}
1801 urls = {}
1802 for line in gitmodules_lines:
1803 if not line:
1804 continue
1805 m = re_path.match(line)
1806 if m:
1807 names.add(m.group(1))
1808 paths[m.group(1)] = m.group(2)
1809 continue
1810 m = re_url.match(line)
1811 if m:
1812 names.add(m.group(1))
1813 urls[m.group(1)] = m.group(2)
1814 continue
1815 names = sorted(names)
1816 return ([paths.get(name, '') for name in names],
1817 [urls.get(name, '') for name in names])
1818
1819 def git_ls_tree(gitdir, rev, paths):
1820 cmd = ['ls-tree', rev, '--']
1821 cmd.extend(paths)
1822 try:
Anthony King7bdac712014-07-16 12:56:40 +01001823 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1824 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001825 except GitError:
1826 return []
1827 if p.Wait() != 0:
1828 return []
1829 objects = {}
1830 for line in p.stdout.split('\n'):
1831 if not line.strip():
1832 continue
1833 object_rev, object_path = line.split()[2:4]
1834 objects[object_path] = object_rev
1835 return objects
1836
1837 try:
1838 rev = self.GetRevisionId()
1839 except GitError:
1840 return []
1841 return get_submodules(self.gitdir, rev)
1842
1843 def GetDerivedSubprojects(self):
1844 result = []
1845 if not self.Exists:
1846 # If git repo does not exist yet, querying its submodules will
1847 # mess up its states; so return here.
1848 return result
1849 for rev, path, url in self._GetSubmodules():
1850 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001851 relpath, worktree, gitdir, objdir = \
1852 self.manifest.GetSubprojectPaths(self, name, path)
1853 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001854 if project:
1855 result.extend(project.GetDerivedSubprojects())
1856 continue
David James8d201162013-10-11 17:03:19 -07001857
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001858 if url.startswith('..'):
1859 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001860 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001861 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001862 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001863 review=self.remote.review,
1864 revision=self.remote.revision)
1865 subproject = Project(manifest=self.manifest,
1866 name=name,
1867 remote=remote,
1868 gitdir=gitdir,
1869 objdir=objdir,
1870 worktree=worktree,
1871 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001872 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001873 revisionId=rev,
1874 rebase=self.rebase,
1875 groups=self.groups,
1876 sync_c=self.sync_c,
1877 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001878 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001879 parent=self,
1880 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001881 result.append(subproject)
1882 result.extend(subproject.GetDerivedSubprojects())
1883 return result
1884
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001885# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001886 def EnableRepositoryExtension(self, key, value='true', version=1):
1887 """Enable git repository extension |key| with |value|.
1888
1889 Args:
1890 key: The extension to enabled. Omit the "extensions." prefix.
1891 value: The value to use for the extension.
1892 version: The minimum git repository version needed.
1893 """
1894 # Make sure the git repo version is new enough already.
1895 found_version = self.config.GetInt('core.repositoryFormatVersion')
1896 if found_version is None:
1897 found_version = 0
1898 if found_version < version:
1899 self.config.SetString('core.repositoryFormatVersion', str(version))
1900
1901 # Enable the extension!
1902 self.config.SetString('extensions.%s' % (key,), value)
1903
Mike Frysinger50a81de2020-09-06 15:51:21 -04001904 def ResolveRemoteHead(self, name=None):
1905 """Find out what the default branch (HEAD) points to.
1906
1907 Normally this points to refs/heads/master, but projects are moving to main.
1908 Support whatever the server uses rather than hardcoding "master" ourselves.
1909 """
1910 if name is None:
1911 name = self.remote.name
1912
1913 # The output will look like (NB: tabs are separators):
1914 # ref: refs/heads/master HEAD
1915 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1916 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1917
1918 for line in output.splitlines():
1919 lhs, rhs = line.split('\t', 1)
1920 if rhs == 'HEAD' and lhs.startswith('ref:'):
1921 return lhs[4:].strip()
1922
1923 return None
1924
Zac Livingstone4332262017-06-16 08:56:09 -06001925 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001926 try:
1927 # if revision (sha or tag) is not present then following function
1928 # throws an error.
Ian Kasprzak0286e312021-02-05 10:06:18 -08001929 self.bare_git.rev_list('-1', '--missing=allow-any',
1930 '%s^0' % self.revisionExpr, '--')
Chris AtLee2fb64662014-01-16 21:32:33 -05001931 return True
1932 except GitError:
1933 # There is no such persistent revision. We have to fetch it.
1934 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001935
Julien Campergue335f5ef2013-10-16 11:02:35 +02001936 def _FetchArchive(self, tarpath, cwd=None):
1937 cmd = ['archive', '-v', '-o', tarpath]
1938 cmd.append('--remote=%s' % self.remote.url)
1939 cmd.append('--prefix=%s/' % self.relpath)
1940 cmd.append(self.revisionExpr)
1941
1942 command = GitCommand(self, cmd, cwd=cwd,
1943 capture_stdout=True,
1944 capture_stderr=True)
1945
1946 if command.Wait() != 0:
1947 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1948
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001949 def _RemoteFetch(self, name=None,
1950 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001951 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001952 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001953 verbose=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001954 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001955 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001956 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001957 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001958 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001959 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001960 clone_filter=None,
1961 retry_fetches=2,
1962 retry_sleep_initial_sec=4.0,
1963 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001964 is_sha1 = False
1965 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001966 # The depth should not be used when fetching to a mirror because
1967 # it will result in a shallow repository that cannot be cloned or
1968 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001969 # The repo project should also never be synced with partial depth.
1970 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1971 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001972
Shawn Pearce69e04d82014-01-29 12:48:54 -08001973 if depth:
1974 current_branch_only = True
1975
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001976 if ID_RE.match(self.revisionExpr) is not None:
1977 is_sha1 = True
1978
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001979 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001980 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001981 # this is a tag and its sha1 value should never change
1982 tag_name = self.revisionExpr[len(R_TAGS):]
1983
1984 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001985 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05001986 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001987 print('Skipped fetching project %s (already have persistent ref)'
1988 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001989 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001990 if is_sha1 and not depth:
1991 # When syncing a specific commit and --depth is not set:
1992 # * if upstream is explicitly specified and is not a sha1, fetch only
1993 # upstream as users expect only upstream to be fetch.
1994 # Note: The commit might not be in upstream in which case the sync
1995 # will fail.
1996 # * otherwise, fetch all branches to make sure we end up with the
1997 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001998 if self.upstream:
1999 current_branch_only = not ID_RE.match(self.upstream)
2000 else:
2001 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002002
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002003 if not name:
2004 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002005
2006 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002007 remote = self.GetRemote(name)
2008 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002009 ssh_proxy = True
2010
Shawn O. Pearce88443382010-10-08 10:02:09 +02002011 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002012 if alt_dir and 'objects' == os.path.basename(alt_dir):
2013 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2015 remote = self.GetRemote(name)
2016
David Pursehouse8a68ff92012-09-24 12:15:13 +09002017 all_refs = self.bare_ref.all
2018 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 tmp = set()
2020
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302021 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002022 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002023 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002024 all_refs[r] = ref_id
2025 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002026 continue
2027
David Pursehouse8a68ff92012-09-24 12:15:13 +09002028 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002029 continue
2030
David Pursehouse8a68ff92012-09-24 12:15:13 +09002031 r = 'refs/_alt/%s' % ref_id
2032 all_refs[r] = ref_id
2033 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034 tmp.add(r)
2035
heping3d7bbc92017-04-12 19:51:47 +08002036 tmp_packed_lines = []
2037 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302039 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002040 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002041 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002042 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002043 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002044
heping3d7bbc92017-04-12 19:51:47 +08002045 tmp_packed = ''.join(tmp_packed_lines)
2046 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002047 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002048 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002049 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002051 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002052
Xin Li745be2e2019-06-03 11:24:30 -07002053 if clone_filter:
2054 git_require((2, 19, 0), fail=True, msg='partial clones')
2055 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002056 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002057
Conley Owensf97e8382015-01-21 11:12:46 -08002058 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002059 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002060 else:
2061 # If this repo has shallow objects, then we don't know which refs have
2062 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2063 # do this with projects that don't have shallow objects, since it is less
2064 # efficient.
2065 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2066 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002067
Mike Frysinger4847e052020-02-22 00:07:35 -05002068 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002069 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002070 if not quiet and sys.stdout.isatty():
2071 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002072 if not self.worktree:
2073 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002074 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002075
Mike Frysingere57f1142019-03-18 21:27:54 -04002076 if force_sync:
2077 cmd.append('--force')
2078
David Pursehouse74cfd272015-10-14 10:50:15 +09002079 if prune:
2080 cmd.append('--prune')
2081
Martin Kellye4e94d22017-03-21 16:05:12 -07002082 if submodules:
2083 cmd.append('--recurse-submodules=on-demand')
2084
Kuang-che Wu6856f982019-11-25 12:37:55 +08002085 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002086 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002088 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002089 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002090 spec.append('tag')
2091 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002092
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302093 if self.manifest.IsMirror and not current_branch_only:
2094 branch = None
2095 else:
2096 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002097 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002098 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002099 # Shallow checkout of a specific commit, fetch from that commit and not
2100 # the heads only as the commit might be deeper in the history.
2101 spec.append(branch)
2102 else:
2103 if is_sha1:
2104 branch = self.upstream
2105 if branch is not None and branch.strip():
2106 if not branch.startswith('refs/'):
2107 branch = R_HEADS + branch
2108 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2109
2110 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2111 # whole repo.
2112 if self.manifest.IsMirror and not spec:
2113 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2114
2115 # If using depth then we should not get all the tags since they may
2116 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002117 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002118 cmd.append('--no-tags')
2119 else:
2120 cmd.append('--tags')
2121 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2122
Conley Owens80b87fe2014-05-09 17:13:44 -07002123 cmd.extend(spec)
2124
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002125 # At least one retry minimum due to git remote prune.
2126 retry_fetches = max(retry_fetches, 2)
2127 retry_cur_sleep = retry_sleep_initial_sec
2128 ok = prune_tried = False
2129 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002130 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger4847e052020-02-22 00:07:35 -05002131 merge_output=True, capture_stdout=quiet)
John L. Villalovos126e2982015-01-29 21:58:12 -08002132 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002133 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002134 ok = True
2135 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002136
2137 # Retry later due to HTTP 429 Too Many Requests.
Mike Frysinger92304bf2021-02-23 03:58:43 -05002138 elif (gitcmd.stdout and
2139 'error:' in gitcmd.stdout and
2140 'HTTP 429' in gitcmd.stdout):
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002141 if not quiet:
2142 print('429 received, sleeping: %s sec' % retry_cur_sleep,
2143 file=sys.stderr)
2144 time.sleep(retry_cur_sleep)
2145 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2146 MAXIMUM_RETRY_SLEEP_SEC)
2147 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2148 RETRY_JITTER_PERCENT))
2149 continue
2150
2151 # If this is not last attempt, try 'git remote prune'.
2152 elif (try_n < retry_fetches - 1 and
Mike Frysinger92304bf2021-02-23 03:58:43 -05002153 gitcmd.stdout and
2154 'error:' in gitcmd.stdout and
2155 'git remote prune' in gitcmd.stdout and
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002156 not prune_tried):
2157 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002158 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002159 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002160 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002161 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002162 break
2163 continue
Brian Harring14a66742012-09-28 20:21:57 -07002164 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002165 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2166 # in sha1 mode, we just tried sync'ing from the upstream field; it
2167 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002168 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002169 elif ret < 0:
2170 # Git died with a signal, exit immediately
2171 break
Mike Frysinger31990f02020-02-17 01:35:18 -05002172 if not verbose:
2173 print('%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002174 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002175
2176 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002177 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002178 if old_packed != '':
2179 _lwrite(packed_refs, old_packed)
2180 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002181 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002182 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002183
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002184 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002185 # We just synced the upstream given branch; verify we
2186 # got what we wanted, else trigger a second run of all
2187 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002188 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002189 # Sync the current branch only with depth set to None.
2190 # We always pass depth=None down to avoid infinite recursion.
2191 return self._RemoteFetch(
2192 name=name, quiet=quiet, verbose=verbose,
2193 current_branch_only=current_branch_only and depth,
2194 initial=False, alt_dir=alt_dir,
2195 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002196
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002197 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002198
Mike Frysingere50b6a72020-02-19 01:45:48 -05002199 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002200 if initial and \
2201 (self.manifest.manifestProject.config.GetString('repo.depth') or
2202 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002203 return False
2204
2205 remote = self.GetRemote(self.remote.name)
2206 bundle_url = remote.url + '/clone.bundle'
2207 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002208 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2209 'persistent-http',
2210 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002211 return False
2212
2213 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2214 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2215
2216 exist_dst = os.path.exists(bundle_dst)
2217 exist_tmp = os.path.exists(bundle_tmp)
2218
2219 if not initial and not exist_dst and not exist_tmp:
2220 return False
2221
2222 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002223 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2224 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002225 if not exist_dst:
2226 return False
2227
2228 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002229 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002230 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002231 if not quiet and sys.stdout.isatty():
2232 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002233 if not self.worktree:
2234 cmd.append('--update-head-ok')
2235 cmd.append(bundle_dst)
2236 for f in remote.fetch:
2237 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002238 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002239
2240 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002241 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002242 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002243 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002244 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002245 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002246
Mike Frysingere50b6a72020-02-19 01:45:48 -05002247 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002248 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002249 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002251 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002252 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002253 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002254 if os.path.exists(tmpPath):
2255 size = os.stat(tmpPath).st_size
2256 if size >= 1024:
2257 cmd += ['--continue-at', '%d' % (size,)]
2258 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002259 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002260 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002261 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002262 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002263 if proxy:
2264 cmd += ['--proxy', proxy]
2265 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2266 cmd += ['--proxy', os.environ['http_proxy']]
2267 if srcUrl.startswith('persistent-https'):
2268 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2269 elif srcUrl.startswith('persistent-http'):
2270 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002271 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002272
Dave Borowitz137d0132015-01-02 11:12:54 -08002273 if IsTrace():
2274 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002275 if verbose:
2276 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2277 stdout = None if verbose else subprocess.PIPE
2278 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002279 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002280 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002281 except OSError:
2282 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002283
Mike Frysingere50b6a72020-02-19 01:45:48 -05002284 (output, _) = proc.communicate()
2285 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002286
Dave Borowitz137d0132015-01-02 11:12:54 -08002287 if curlret == 22:
2288 # From curl man page:
2289 # 22: HTTP page not retrieved. The requested url was not found or
2290 # returned another error with the HTTP error code being 400 or above.
2291 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002292 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002293 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2294 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002295 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002296 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002297 elif curlret and not verbose and output:
2298 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002299
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002300 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002301 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002302 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002303 return True
2304 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002305 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002306 return False
2307 else:
2308 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002309
Kris Giesingc8d882a2014-12-23 13:02:32 -08002310 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002311 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002312 with open(path, 'rb') as f:
2313 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002314 return True
2315 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002316 if not quiet:
2317 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002318 return False
2319 except OSError:
2320 return False
2321
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002322 def _Checkout(self, rev, quiet=False):
2323 cmd = ['checkout']
2324 if quiet:
2325 cmd.append('-q')
2326 cmd.append(rev)
2327 cmd.append('--')
2328 if GitCommand(self, cmd).Wait() != 0:
2329 if self._allrefs:
2330 raise GitError('%s checkout %s ' % (self.name, rev))
2331
Mike Frysinger915fda12020-03-22 12:15:20 -04002332 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002333 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002334 if ffonly:
2335 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002336 if record_origin:
2337 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002338 cmd.append(rev)
2339 cmd.append('--')
2340 if GitCommand(self, cmd).Wait() != 0:
2341 if self._allrefs:
2342 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2343
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302344 def _LsRemote(self, refs):
2345 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302346 p = GitCommand(self, cmd, capture_stdout=True)
2347 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002348 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302349 return None
2350
Anthony King7bdac712014-07-16 12:56:40 +01002351 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002352 cmd = ['revert']
2353 cmd.append('--no-edit')
2354 cmd.append(rev)
2355 cmd.append('--')
2356 if GitCommand(self, cmd).Wait() != 0:
2357 if self._allrefs:
2358 raise GitError('%s revert %s ' % (self.name, rev))
2359
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002360 def _ResetHard(self, rev, quiet=True):
2361 cmd = ['reset', '--hard']
2362 if quiet:
2363 cmd.append('-q')
2364 cmd.append(rev)
2365 if GitCommand(self, cmd).Wait() != 0:
2366 raise GitError('%s reset --hard %s ' % (self.name, rev))
2367
Martin Kellye4e94d22017-03-21 16:05:12 -07002368 def _SyncSubmodules(self, quiet=True):
2369 cmd = ['submodule', 'update', '--init', '--recursive']
2370 if quiet:
2371 cmd.append('-q')
2372 if GitCommand(self, cmd).Wait() != 0:
2373 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2374
Anthony King7bdac712014-07-16 12:56:40 +01002375 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002376 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002377 if onto is not None:
2378 cmd.extend(['--onto', onto])
2379 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002380 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002381 raise GitError('%s rebase %s ' % (self.name, upstream))
2382
Pierre Tardy3d125942012-05-04 12:18:12 +02002383 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002384 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002385 if ffonly:
2386 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002387 if GitCommand(self, cmd).Wait() != 0:
2388 raise GitError('%s merge %s ' % (self.name, head))
2389
David Pursehousee8ace262020-02-13 12:41:15 +09002390 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002391 init_git_dir = not os.path.exists(self.gitdir)
2392 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002393 try:
2394 # Initialize the bare repository, which contains all of the objects.
2395 if init_obj_dir:
2396 os.makedirs(self.objdir)
2397 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002398
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002399 if self.use_git_worktrees:
2400 # Set up the m/ space to point to the worktree-specific ref space.
2401 # We'll update the worktree-specific ref space on each checkout.
2402 if self.manifest.branch:
2403 self.bare_git.symbolic_ref(
2404 '-m', 'redirecting to worktree scope',
2405 R_M + self.manifest.branch,
2406 R_WORKTREE_M + self.manifest.branch)
2407
2408 # Enable per-worktree config file support if possible. This is more a
2409 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002410 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002411 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002412
Kevin Degib1a07b82015-07-27 13:33:43 -06002413 # If we have a separate directory to hold refs, initialize it as well.
2414 if self.objdir != self.gitdir:
2415 if init_git_dir:
2416 os.makedirs(self.gitdir)
2417
2418 if init_obj_dir or init_git_dir:
2419 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2420 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002421 try:
2422 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2423 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002424 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002425 print("Retrying clone after deleting %s" %
2426 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002427 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002428 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2429 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002430 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002431 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002432 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2433 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002434 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002435 raise e
2436 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002437
Kevin Degi384b3c52014-10-16 16:02:58 -06002438 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002439 mp = self.manifest.manifestProject
2440 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002441
Kevin Degib1a07b82015-07-27 13:33:43 -06002442 if ref_dir or mirror_git:
2443 if not mirror_git:
2444 mirror_git = os.path.join(ref_dir, self.name + '.git')
2445 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2446 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002447 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2448 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002449
Kevin Degib1a07b82015-07-27 13:33:43 -06002450 if os.path.exists(mirror_git):
2451 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002452 elif os.path.exists(repo_git):
2453 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002454 elif os.path.exists(worktrees_git):
2455 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002456 else:
2457 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002458
Kevin Degib1a07b82015-07-27 13:33:43 -06002459 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002460 if not os.path.isabs(ref_dir):
2461 # The alternate directory is relative to the object database.
2462 ref_dir = os.path.relpath(ref_dir,
2463 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002464 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2465 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002466
David Pursehousee8ace262020-02-13 12:41:15 +09002467 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002468
2469 m = self.manifest.manifestProject.config
2470 for key in ['user.name', 'user.email']:
2471 if m.Has(key, include_defaults=False):
2472 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002473 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002474 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Mike Frysinger38867fb2021-02-09 23:14:41 -05002475 self.config.SetBoolean('core.bare', True if self.manifest.IsMirror else None)
Kevin Degib1a07b82015-07-27 13:33:43 -06002476 except Exception:
2477 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002478 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002479 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002480 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002481 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002482
David Pursehousee8ace262020-02-13 12:41:15 +09002483 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002484 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002485 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002486
David Pursehousee8ace262020-02-13 12:41:15 +09002487 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002488 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002489 if not os.path.exists(hooks):
2490 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002491 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002492 name = os.path.basename(stock_hook)
2493
Victor Boivie65e0f352011-04-18 11:23:29 +02002494 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002495 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002496 # Don't install a Gerrit Code Review hook if this
2497 # project does not appear to use it for reviews.
2498 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002499 # Since the manifest project is one of those, but also
2500 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002501 continue
2502
2503 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002504 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002505 continue
2506 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002507 # If the files are the same, we'll leave it alone. We create symlinks
2508 # below by default but fallback to hardlinks if the OS blocks them.
2509 # So if we're here, it's probably because we made a hardlink below.
2510 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002511 if not quiet:
2512 _warn("%s: Not replacing locally modified %s hook",
2513 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002514 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002515 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002516 platform_utils.symlink(
2517 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002518 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002519 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002520 try:
2521 os.link(stock_hook, dst)
2522 except OSError:
2523 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002524 else:
2525 raise
2526
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002527 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002528 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002529 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002530 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002531 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002532 remote.review = self.remote.review
2533 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002534
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002535 if self.worktree:
2536 remote.ResetFetch(mirror=False)
2537 else:
2538 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539 remote.Save()
2540
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002541 def _InitMRef(self):
2542 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002543 if self.use_git_worktrees:
2544 # We can't update this ref with git worktrees until it exists.
2545 # We'll wait until the initial checkout to set it.
2546 if not os.path.exists(self.worktree):
2547 return
2548
2549 base = R_WORKTREE_M
2550 active_git = self.work_git
Remy Böhmer1469c282020-12-15 18:49:02 +01002551
2552 self._InitAnyMRef(HEAD, self.bare_git, detach=True)
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002553 else:
2554 base = R_M
2555 active_git = self.bare_git
2556
2557 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002558
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002559 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002560 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002561
Remy Böhmer1469c282020-12-15 18:49:02 +01002562 def _InitAnyMRef(self, ref, active_git, detach=False):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002563 cur = self.bare_ref.symref(ref)
2564
2565 if self.revisionId:
2566 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2567 msg = 'manifest set to %s' % self.revisionId
2568 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002569 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002570 else:
2571 remote = self.GetRemote(self.remote.name)
2572 dst = remote.ToLocal(self.revisionExpr)
2573 if cur != dst:
2574 msg = 'manifest set to %s' % self.revisionExpr
Remy Böhmer1469c282020-12-15 18:49:02 +01002575 if detach:
2576 active_git.UpdateRef(ref, dst, message=msg, detach=True)
2577 else:
2578 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002579
Kevin Degi384b3c52014-10-16 16:02:58 -06002580 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002581 # Git worktrees don't use symlinks to share at all.
2582 if self.use_git_worktrees:
2583 return
2584
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002585 symlink_files = self.shareable_files[:]
2586 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002587 if share_refs:
2588 symlink_files += self.working_tree_files
2589 symlink_dirs += self.working_tree_dirs
2590 to_symlink = symlink_files + symlink_dirs
2591 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002592 # Try to self-heal a bit in simple cases.
2593 dst_path = os.path.join(destdir, name)
2594 src_path = os.path.join(srcdir, name)
2595
2596 if name in self.working_tree_dirs:
2597 # If the dir is missing under .repo/projects/, create it.
2598 if not os.path.exists(src_path):
2599 os.makedirs(src_path)
2600
2601 elif name in self.working_tree_files:
2602 # If it's a file under the checkout .git/ and the .repo/projects/ has
2603 # nothing, move the file under the .repo/projects/ tree.
2604 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2605 platform_utils.rename(dst_path, src_path)
2606
2607 # If the path exists under the .repo/projects/ and there's no symlink
2608 # under the checkout .git/, recreate the symlink.
2609 if name in self.working_tree_dirs or name in self.working_tree_files:
2610 if os.path.exists(src_path) and not os.path.exists(dst_path):
2611 platform_utils.symlink(
2612 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2613
2614 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002615 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002616 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002617 # Fail if the links are pointing to the wrong place
2618 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002619 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002620 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002621 'work tree. If you\'re comfortable with the '
2622 'possibility of losing the work tree\'s git metadata,'
2623 ' use `repo sync --force-sync {0}` to '
2624 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002625
David James8d201162013-10-11 17:03:19 -07002626 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2627 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2628
2629 Args:
2630 gitdir: The bare git repository. Must already be initialized.
2631 dotgit: The repository you would like to initialize.
2632 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2633 Only one work tree can store refs under a given |gitdir|.
2634 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2635 This saves you the effort of initializing |dotgit| yourself.
2636 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002637 symlink_files = self.shareable_files[:]
2638 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002639 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002640 symlink_files += self.working_tree_files
2641 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002642 to_symlink = symlink_files + symlink_dirs
2643
2644 to_copy = []
2645 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002646 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002647
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002648 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002649 for name in set(to_copy).union(to_symlink):
2650 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002651 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002652 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002653
Kevin Degi384b3c52014-10-16 16:02:58 -06002654 if os.path.lexists(dst):
2655 continue
David James8d201162013-10-11 17:03:19 -07002656
2657 # If the source dir doesn't exist, create an empty dir.
2658 if name in symlink_dirs and not os.path.lexists(src):
2659 os.makedirs(src)
2660
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002661 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002662 platform_utils.symlink(
2663 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002664 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002665 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002666 shutil.copytree(src, dst)
2667 elif os.path.isfile(src):
2668 shutil.copy(src, dst)
2669
Conley Owens80b87fe2014-05-09 17:13:44 -07002670 # If the source file doesn't exist, ensure the destination
2671 # file doesn't either.
2672 if name in symlink_files and not os.path.lexists(src):
2673 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002674 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002675 except OSError:
2676 pass
2677
David James8d201162013-10-11 17:03:19 -07002678 except OSError as e:
2679 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002680 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002681 else:
2682 raise
2683
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002684 def _InitGitWorktree(self):
2685 """Init the project using git worktrees."""
2686 self.bare_git.worktree('prune')
2687 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2688 self.worktree, self.GetRevisionId())
2689
2690 # Rewrite the internal state files to use relative paths between the
2691 # checkouts & worktrees.
2692 dotgit = os.path.join(self.worktree, '.git')
2693 with open(dotgit, 'r') as fp:
2694 # Figure out the checkout->worktree path.
2695 setting = fp.read()
2696 assert setting.startswith('gitdir:')
2697 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002698 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2699 # of file permissions. Delete it and recreate it from scratch to avoid.
2700 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002701 # Use relative path from checkout->worktree & maintain Unix line endings
2702 # on all OS's to match git behavior.
2703 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002704 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2705 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002706 # Use relative path from worktree->checkout & maintain Unix line endings
2707 # on all OS's to match git behavior.
2708 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002709 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2710
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002711 self._InitMRef()
2712
Martin Kellye4e94d22017-03-21 16:05:12 -07002713 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002714 realdotgit = os.path.join(self.worktree, '.git')
2715 tmpdotgit = realdotgit + '.tmp'
2716 init_dotgit = not os.path.exists(realdotgit)
2717 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002718 if self.use_git_worktrees:
2719 self._InitGitWorktree()
2720 self._CopyAndLinkFiles()
2721 return
2722
Mike Frysingerf4545122019-11-11 04:34:16 -05002723 dotgit = tmpdotgit
2724 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2725 os.makedirs(tmpdotgit)
2726 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2727 copy_all=False)
2728 else:
2729 dotgit = realdotgit
2730
Kevin Degib1a07b82015-07-27 13:33:43 -06002731 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002732 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2733 except GitError as e:
2734 if force_sync and not init_dotgit:
2735 try:
2736 platform_utils.rmtree(dotgit)
2737 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002738 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002739 raise e
2740 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741
Mike Frysingerf4545122019-11-11 04:34:16 -05002742 if init_dotgit:
2743 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002744
Mike Frysingerf4545122019-11-11 04:34:16 -05002745 # Now that the .git dir is fully set up, move it to its final home.
2746 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002747
Mike Frysingerf4545122019-11-11 04:34:16 -05002748 # Finish checking out the worktree.
2749 cmd = ['read-tree', '--reset', '-u']
2750 cmd.append('-v')
2751 cmd.append(HEAD)
2752 if GitCommand(self, cmd).Wait() != 0:
2753 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002754
Mike Frysingerf4545122019-11-11 04:34:16 -05002755 if submodules:
2756 self._SyncSubmodules(quiet=True)
2757 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002758
Renaud Paquay788e9622017-01-27 11:41:12 -08002759 def _get_symlink_error_message(self):
2760 if platform_utils.isWindows():
2761 return ('Unable to create symbolic link. Please re-run the command as '
2762 'Administrator, or see '
2763 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2764 'for other options.')
2765 return 'filesystem must support symlinks'
2766
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002767 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002768 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002769
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002770 def _revlist(self, *args, **kw):
2771 a = []
2772 a.extend(args)
2773 a.append('--')
2774 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002775
2776 @property
2777 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002778 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002780 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002781 """Get logs between two revisions of this project."""
2782 comp = '..'
2783 if rev1:
2784 revs = [rev1]
2785 if rev2:
2786 revs.extend([comp, rev2])
2787 cmd = ['log', ''.join(revs)]
2788 out = DiffColoring(self.config)
2789 if out.is_on and color:
2790 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002791 if pretty_format is not None:
2792 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002793 if oneline:
2794 cmd.append('--oneline')
2795
2796 try:
2797 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2798 if log.Wait() == 0:
2799 return log.stdout
2800 except GitError:
2801 # worktree may not exist if groups changed for example. In that case,
2802 # try in gitdir instead.
2803 if not os.path.exists(self.worktree):
2804 return self.bare_git.log(*cmd[1:])
2805 else:
2806 raise
2807 return None
2808
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002809 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2810 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002811 """Get the list of logs from this revision to given revisionId"""
2812 logs = {}
2813 selfId = self.GetRevisionId(self._allrefs)
2814 toId = toProject.GetRevisionId(toProject._allrefs)
2815
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002816 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2817 pretty_format=pretty_format)
2818 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2819 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002820 return logs
2821
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002822 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002823
David James8d201162013-10-11 17:03:19 -07002824 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002825 self._project = project
2826 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002827 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002829 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2830 def __getstate__(self):
2831 return (self._project, self._bare, self._gitdir)
2832
2833 def __setstate__(self, state):
2834 self._project, self._bare, self._gitdir = state
2835
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836 def LsOthers(self):
2837 p = GitCommand(self._project,
2838 ['ls-files',
2839 '-z',
2840 '--others',
2841 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002842 bare=False,
David James8d201162013-10-11 17:03:19 -07002843 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002844 capture_stdout=True,
2845 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002846 if p.Wait() == 0:
2847 out = p.stdout
2848 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002849 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002850 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002851 return []
2852
2853 def DiffZ(self, name, *args):
2854 cmd = [name]
2855 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002856 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 cmd.extend(args)
2858 p = GitCommand(self._project,
2859 cmd,
David James8d201162013-10-11 17:03:19 -07002860 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002861 bare=False,
2862 capture_stdout=True,
2863 capture_stderr=True)
Mike Frysinger84230002021-02-16 17:08:35 -05002864 p.Wait()
2865 r = {}
2866 out = p.stdout
2867 if out:
2868 out = iter(out[:-1].split('\0'))
2869 while out:
2870 try:
2871 info = next(out)
2872 path = next(out)
2873 except StopIteration:
2874 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002875
Mike Frysinger84230002021-02-16 17:08:35 -05002876 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002877
Mike Frysinger84230002021-02-16 17:08:35 -05002878 def __init__(self, path, omode, nmode, oid, nid, state):
2879 self.path = path
2880 self.src_path = None
2881 self.old_mode = omode
2882 self.new_mode = nmode
2883 self.old_id = oid
2884 self.new_id = nid
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002885
Mike Frysinger84230002021-02-16 17:08:35 -05002886 if len(state) == 1:
2887 self.status = state
2888 self.level = None
2889 else:
2890 self.status = state[:1]
2891 self.level = state[1:]
2892 while self.level.startswith('0'):
2893 self.level = self.level[1:]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002894
Mike Frysinger84230002021-02-16 17:08:35 -05002895 info = info[1:].split(' ')
2896 info = _Info(path, *info)
2897 if info.status in ('R', 'C'):
2898 info.src_path = info.path
2899 info.path = next(out)
2900 r[info.path] = info
2901 return r
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002902
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002903 def GetDotgitPath(self, subpath=None):
2904 """Return the full path to the .git dir.
2905
2906 As a convenience, append |subpath| if provided.
2907 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002908 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002909 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002910 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002911 dotgit = os.path.join(self._project.worktree, '.git')
2912 if os.path.isfile(dotgit):
2913 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2914 with open(dotgit) as fp:
2915 setting = fp.read()
2916 assert setting.startswith('gitdir:')
2917 gitdir = setting.split(':', 1)[1].strip()
2918 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2919
2920 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2921
2922 def GetHead(self):
2923 """Return the ref that HEAD points to."""
2924 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002925 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002926 with open(path) as fd:
2927 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002928 except IOError as e:
2929 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002930 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302931 line = line.decode()
2932 except AttributeError:
2933 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002934 if line.startswith('ref: '):
2935 return line[5:-1]
2936 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002937
2938 def SetHead(self, ref, message=None):
2939 cmdv = []
2940 if message is not None:
2941 cmdv.extend(['-m', message])
2942 cmdv.append(HEAD)
2943 cmdv.append(ref)
2944 self.symbolic_ref(*cmdv)
2945
2946 def DetachHead(self, new, message=None):
2947 cmdv = ['--no-deref']
2948 if message is not None:
2949 cmdv.extend(['-m', message])
2950 cmdv.append(HEAD)
2951 cmdv.append(new)
2952 self.update_ref(*cmdv)
2953
2954 def UpdateRef(self, name, new, old=None,
2955 message=None,
2956 detach=False):
2957 cmdv = []
2958 if message is not None:
2959 cmdv.extend(['-m', message])
2960 if detach:
2961 cmdv.append('--no-deref')
2962 cmdv.append(name)
2963 cmdv.append(new)
2964 if old is not None:
2965 cmdv.append(old)
2966 self.update_ref(*cmdv)
2967
2968 def DeleteRef(self, name, old=None):
2969 if not old:
2970 old = self.rev_parse(name)
2971 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002972 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002973
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002974 def rev_list(self, *args, **kw):
2975 if 'format' in kw:
2976 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2977 else:
2978 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002979 cmdv.extend(args)
2980 p = GitCommand(self._project,
2981 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002982 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002983 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002984 capture_stdout=True,
2985 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002986 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002987 raise GitError('%s rev-list %s: %s' %
2988 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002989 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002990
2991 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002992 """Allow arbitrary git commands using pythonic syntax.
2993
2994 This allows you to do things like:
2995 git_obj.rev_parse('HEAD')
2996
2997 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2998 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002999 Any other positional arguments will be passed to the git command, and the
3000 following keyword arguments are supported:
3001 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003002
3003 Args:
3004 name: The name of the git command to call. Any '_' characters will
3005 be replaced with '-'.
3006
3007 Returns:
3008 A callable object that will try to call git with the named command.
3009 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003010 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003011
Dave Borowitz091f8932012-10-23 17:01:04 -07003012 def runner(*args, **kwargs):
3013 cmdv = []
3014 config = kwargs.pop('config', None)
3015 for k in kwargs:
3016 raise TypeError('%s() got an unexpected keyword argument %r'
3017 % (name, k))
3018 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303019 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003020 cmdv.append('-c')
3021 cmdv.append('%s=%s' % (k, v))
3022 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003023 cmdv.extend(args)
3024 p = GitCommand(self._project,
3025 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003026 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003027 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003028 capture_stdout=True,
3029 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003030 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003031 raise GitError('%s %s: %s' %
3032 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003033 r = p.stdout
3034 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3035 return r[:-1]
3036 return r
3037 return runner
3038
3039
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003040class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003041
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003042 def __str__(self):
3043 return 'prior sync failed; rebase still in progress'
3044
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003045
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003046class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003047
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003048 def __str__(self):
3049 return 'contains uncommitted changes'
3050
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003051
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003052class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003053
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003054 def __init__(self, project, text):
3055 self.project = project
3056 self.text = text
3057
3058 def Print(self, syncbuf):
3059 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3060 syncbuf.out.nl()
3061
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003062
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003063class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003064
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003065 def __init__(self, project, why):
3066 self.project = project
3067 self.why = why
3068
3069 def Print(self, syncbuf):
3070 syncbuf.out.fail('error: %s/: %s',
3071 self.project.relpath,
3072 str(self.why))
3073 syncbuf.out.nl()
3074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003075
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003076class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003077
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003078 def __init__(self, project, action):
3079 self.project = project
3080 self.action = action
3081
3082 def Run(self, syncbuf):
3083 out = syncbuf.out
3084 out.project('project %s/', self.project.relpath)
3085 out.nl()
3086 try:
3087 self.action()
3088 out.nl()
3089 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003090 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003091 out.nl()
3092 return False
3093
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003094
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003095class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003096
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003097 def __init__(self, config):
Mike Frysinger5d9c4972021-02-19 13:34:09 -05003098 super().__init__(config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003099 self.project = self.printer('header', attr='bold')
3100 self.info = self.printer('info')
3101 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003102
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003103
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003104class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003105
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003106 def __init__(self, config, detach_head=False):
3107 self._messages = []
3108 self._failures = []
3109 self._later_queue1 = []
3110 self._later_queue2 = []
3111
3112 self.out = _SyncColoring(config)
3113 self.out.redirect(sys.stderr)
3114
3115 self.detach_head = detach_head
3116 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003117 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003118
3119 def info(self, project, fmt, *args):
3120 self._messages.append(_InfoMessage(project, fmt % args))
3121
3122 def fail(self, project, err=None):
3123 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003124 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003125
3126 def later1(self, project, what):
3127 self._later_queue1.append(_Later(project, what))
3128
3129 def later2(self, project, what):
3130 self._later_queue2.append(_Later(project, what))
3131
3132 def Finish(self):
3133 self._PrintMessages()
3134 self._RunLater()
3135 self._PrintMessages()
3136 return self.clean
3137
David Rileye0684ad2017-04-05 00:02:59 -07003138 def Recently(self):
3139 recent_clean = self.recent_clean
3140 self.recent_clean = True
3141 return recent_clean
3142
3143 def _MarkUnclean(self):
3144 self.clean = False
3145 self.recent_clean = False
3146
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003147 def _RunLater(self):
3148 for q in ['_later_queue1', '_later_queue2']:
3149 if not self._RunQueue(q):
3150 return
3151
3152 def _RunQueue(self, queue):
3153 for m in getattr(self, queue):
3154 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003155 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003156 return False
3157 setattr(self, queue, [])
3158 return True
3159
3160 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003161 if self._messages or self._failures:
3162 if os.isatty(2):
3163 self.out.write(progress.CSI_ERASE_LINE)
3164 self.out.write('\r')
3165
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003166 for m in self._messages:
3167 m.Print(self)
3168 for m in self._failures:
3169 m.Print(self)
3170
3171 self._messages = []
3172 self._failures = []
3173
3174
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003175class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003176
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003177 """A special project housed under .repo.
3178 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003179
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003180 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003181 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003182 manifest=manifest,
3183 name=name,
3184 gitdir=gitdir,
3185 objdir=gitdir,
3186 worktree=worktree,
3187 remote=RemoteSpec('origin'),
3188 relpath='.repo/%s' % name,
3189 revisionExpr='refs/heads/master',
3190 revisionId=None,
3191 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003192
3193 def PreSync(self):
3194 if self.Exists:
3195 cb = self.CurrentBranch
3196 if cb:
3197 base = self.GetBranch(cb).merge
3198 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003199 self.revisionExpr = base
3200 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003201
Martin Kelly224a31a2017-07-10 14:46:25 -07003202 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003203 """ Prepare MetaProject for manifest branch switch
3204 """
3205
3206 # detach and delete manifest branch, allowing a new
3207 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003208 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003209 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003210 syncbuf.Finish()
3211
3212 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003213 ['update-ref', '-d', 'refs/heads/default'],
3214 capture_stdout=True,
3215 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003216
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003217 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003218 def LastFetch(self):
3219 try:
3220 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3221 return os.path.getmtime(fh)
3222 except OSError:
3223 return 0
3224
3225 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003226 def HasChanges(self):
3227 """Has the remote received new commits not yet checked out?
3228 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003229 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003230 return False
3231
David Pursehouse8a68ff92012-09-24 12:15:13 +09003232 all_refs = self.bare_ref.all
3233 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003234 head = self.work_git.GetHead()
3235 if head.startswith(R_HEADS):
3236 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003237 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003238 except KeyError:
3239 head = None
3240
3241 if revid == head:
3242 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003243 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003244 return True
3245 return False