blob: ad7d59ae918c7764856bb13f2d4b28aa8b2a6732 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
2#
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080018import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070020import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070022import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import re
24import shutil
25import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070026import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020028import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080029import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070030import time
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070031
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070032from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070033from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070034from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
35 ID_RE
Remy Bohmer16c13282020-09-10 10:38:04 +020036from error import GitError, UploadError, DownloadError
Mike Frysingere6a202f2019-08-02 15:57:57 -040037from error import ManifestInvalidRevisionError, ManifestInvalidPathError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070039import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040040import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040041from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
Mike Frysinger21b7fbe2020-02-26 23:53:36 -050043from 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 -070044
David Pursehouse59bbb582013-05-17 10:49:33 +090045from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040046if is_python3():
47 import urllib.parse
48else:
49 import imp
50 import urlparse
51 urllib = imp.new_module('urllib')
52 urllib.parse = urlparse
David Pursehousea46bf7d2020-02-15 12:45:53 +090053 input = raw_input # noqa: F821
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
George Engelbrecht9bc283e2020-04-02 12:36:09 -060056# Maximum sleep time allowed during retries.
57MAXIMUM_RETRY_SLEEP_SEC = 3600.0
58# +-10% random jitter is added to each Fetches retry sleep duration.
59RETRY_JITTER_PERCENT = 0.1
60
61
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062def _lwrite(path, content):
63 lock = '%s.lock' % path
64
Mike Frysinger3164d402019-11-11 05:40:22 -050065 with open(lock, 'w') as fd:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070066 fd.write(content)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067
68 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070069 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070070 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080071 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070072 raise
73
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070074
Shawn O. Pearce48244782009-04-16 08:25:57 -070075def _error(fmt, *args):
76 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070077 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070078
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070079
David Pursehousef33929d2015-08-24 14:39:14 +090080def _warn(fmt, *args):
81 msg = fmt % args
82 print('warn: %s' % msg, file=sys.stderr)
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070085def not_rev(r):
86 return '^' + r
87
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070088
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080089def sq(r):
90 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080091
David Pursehouse819827a2020-02-12 15:20:19 +090092
Jonathan Nieder93719792015-03-17 11:29:58 -070093_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070094
95
Jonathan Nieder93719792015-03-17 11:29:58 -070096def _ProjectHooks():
97 """List the hooks present in the 'hooks' directory.
98
99 These hooks are project hooks and are copied to the '.git/hooks' directory
100 of all subprojects.
101
102 This function caches the list of hooks (based on the contents of the
103 'repo/hooks' directory) on the first call.
104
105 Returns:
106 A list of absolute paths to all of the files in the hooks directory.
107 """
108 global _project_hook_list
109 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700110 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700111 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700112 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700113 return _project_hook_list
114
115
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700116class DownloadedChange(object):
117 _commit_cache = None
118
119 def __init__(self, project, base, change_id, ps_id, commit):
120 self.project = project
121 self.base = base
122 self.change_id = change_id
123 self.ps_id = ps_id
124 self.commit = commit
125
126 @property
127 def commits(self):
128 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700129 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
130 '--abbrev-commit',
131 '--pretty=oneline',
132 '--reverse',
133 '--date-order',
134 not_rev(self.base),
135 self.commit,
136 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700137 return self._commit_cache
138
139
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700140class ReviewableBranch(object):
141 _commit_cache = None
Mike Frysinger6da17752019-09-11 18:43:17 -0400142 _base_exists = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700143
144 def __init__(self, project, branch, base):
145 self.project = project
146 self.branch = branch
147 self.base = base
148
149 @property
150 def name(self):
151 return self.branch.name
152
153 @property
154 def commits(self):
155 if self._commit_cache is None:
Mike Frysinger6da17752019-09-11 18:43:17 -0400156 args = ('--abbrev=8', '--abbrev-commit', '--pretty=oneline', '--reverse',
157 '--date-order', not_rev(self.base), R_HEADS + self.name, '--')
158 try:
159 self._commit_cache = self.project.bare_git.rev_list(*args)
160 except GitError:
161 # We weren't able to probe the commits for this branch. Was it tracking
162 # a branch that no longer exists? If so, return no commits. Otherwise,
163 # rethrow the error as we don't know what's going on.
164 if self.base_exists:
165 raise
166
167 self._commit_cache = []
168
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169 return self._commit_cache
170
171 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800172 def unabbrev_commits(self):
173 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700174 for commit in self.project.bare_git.rev_list(not_rev(self.base),
175 R_HEADS + self.name,
176 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800177 r[commit[0:8]] = commit
178 return r
179
180 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700181 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700182 return self.project.bare_git.log('--pretty=format:%cd',
183 '-n', '1',
184 R_HEADS + self.name,
185 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700186
Mike Frysinger6da17752019-09-11 18:43:17 -0400187 @property
188 def base_exists(self):
189 """Whether the branch we're tracking exists.
190
191 Normally it should, but sometimes branches we track can get deleted.
192 """
193 if self._base_exists is None:
194 try:
195 self.project.bare_git.rev_parse('--verify', not_rev(self.base))
196 # If we're still here, the base branch exists.
197 self._base_exists = True
198 except GitError:
199 # If we failed to verify, the base branch doesn't exist.
200 self._base_exists = False
201
202 return self._base_exists
203
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700204 def UploadForReview(self, people,
Mike Frysinger819cc812020-02-19 02:27:22 -0500205 dryrun=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700206 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500207 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500208 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200209 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700210 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200211 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200212 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800213 validate_certs=True,
214 push_options=None):
Mike Frysinger819cc812020-02-19 02:27:22 -0500215 self.project.UploadForReview(branch=self.name,
216 people=people,
217 dryrun=dryrun,
Brian Harring435370c2012-07-28 15:37:04 -0700218 auto_topic=auto_topic,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500219 hashtags=hashtags,
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500220 labels=labels,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200221 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700222 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200223 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200224 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800225 validate_certs=validate_certs,
226 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700228 def GetPublishedRefs(self):
229 refs = {}
230 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231 self.branch.remote.SshReviewUrl(self.project.UserEmail),
232 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700233 for line in output.split('\n'):
234 try:
235 (sha, ref) = line.split()
236 refs[sha] = ref
237 except ValueError:
238 pass
239
240 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700241
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700242
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700244
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700245 def __init__(self, config):
246 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100247 self.project = self.printer('header', attr='bold')
248 self.branch = self.printer('header', attr='bold')
249 self.nobranch = self.printer('nobranch', fg='red')
250 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251
Anthony King7bdac712014-07-16 12:56:40 +0100252 self.added = self.printer('added', fg='green')
253 self.changed = self.printer('changed', fg='red')
254 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700255
256
257class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700258
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700259 def __init__(self, config):
260 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100261 self.project = self.printer('header', attr='bold')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400262 self.fail = self.printer('fail', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Anthony King7bdac712014-07-16 12:56:40 +0100265class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700266
James W. Mills24c13082012-04-12 15:04:13 -0500267 def __init__(self, name, value, keep):
268 self.name = name
269 self.value = value
270 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700272
Mike Frysingere6a202f2019-08-02 15:57:57 -0400273def _SafeExpandPath(base, subpath, skipfinal=False):
274 """Make sure |subpath| is completely safe under |base|.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700275
Mike Frysingere6a202f2019-08-02 15:57:57 -0400276 We make sure no intermediate symlinks are traversed, and that the final path
277 is not a special file (e.g. not a socket or fifo).
278
279 NB: We rely on a number of paths already being filtered out while parsing the
280 manifest. See the validation logic in manifest_xml.py for more details.
281 """
Mike Frysingerd9254592020-02-19 22:36:26 -0500282 # Split up the path by its components. We can't use os.path.sep exclusively
283 # as some platforms (like Windows) will convert / to \ and that bypasses all
284 # our constructed logic here. Especially since manifest authors only use
285 # / in their paths.
286 resep = re.compile(r'[/%s]' % re.escape(os.path.sep))
287 components = resep.split(subpath)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400288 if skipfinal:
289 # Whether the caller handles the final component itself.
290 finalpart = components.pop()
291
292 path = base
293 for part in components:
294 if part in {'.', '..'}:
295 raise ManifestInvalidPathError(
296 '%s: "%s" not allowed in paths' % (subpath, part))
297
298 path = os.path.join(path, part)
299 if platform_utils.islink(path):
300 raise ManifestInvalidPathError(
301 '%s: traversing symlinks not allow' % (path,))
302
303 if os.path.exists(path):
304 if not os.path.isfile(path) and not platform_utils.isdir(path):
305 raise ManifestInvalidPathError(
306 '%s: only regular files & directories allowed' % (path,))
307
308 if skipfinal:
309 path = os.path.join(path, finalpart)
310
311 return path
312
313
314class _CopyFile(object):
315 """Container for <copyfile> manifest element."""
316
317 def __init__(self, git_worktree, src, topdir, dest):
318 """Register a <copyfile> request.
319
320 Args:
321 git_worktree: Absolute path to the git project checkout.
322 src: Relative path under |git_worktree| of file to read.
323 topdir: Absolute path to the top of the repo client checkout.
324 dest: Relative path under |topdir| of file to write.
325 """
326 self.git_worktree = git_worktree
327 self.topdir = topdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700328 self.src = src
329 self.dest = dest
330
331 def _Copy(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400332 src = _SafeExpandPath(self.git_worktree, self.src)
333 dest = _SafeExpandPath(self.topdir, self.dest)
334
335 if platform_utils.isdir(src):
336 raise ManifestInvalidPathError(
337 '%s: copying from directory not supported' % (self.src,))
338 if platform_utils.isdir(dest):
339 raise ManifestInvalidPathError(
340 '%s: copying to directory not allowed' % (self.dest,))
341
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700342 # copy file if it does not exist or is out of date
343 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
344 try:
345 # remove existing file first, since it might be read-only
346 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800347 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400348 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200349 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700350 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200351 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700352 shutil.copy(src, dest)
353 # make the file read-only
354 mode = os.stat(dest)[stat.ST_MODE]
355 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
356 os.chmod(dest, mode)
357 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700358 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700359
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700360
Anthony King7bdac712014-07-16 12:56:40 +0100361class _LinkFile(object):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400362 """Container for <linkfile> manifest element."""
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700363
Mike Frysingere6a202f2019-08-02 15:57:57 -0400364 def __init__(self, git_worktree, src, topdir, dest):
365 """Register a <linkfile> request.
366
367 Args:
368 git_worktree: Absolute path to the git project checkout.
369 src: Target of symlink relative to path under |git_worktree|.
370 topdir: Absolute path to the top of the repo client checkout.
371 dest: Relative path under |topdir| of symlink to create.
372 """
Wink Saville4c426ef2015-06-03 08:05:17 -0700373 self.git_worktree = git_worktree
Mike Frysingere6a202f2019-08-02 15:57:57 -0400374 self.topdir = topdir
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500375 self.src = src
376 self.dest = dest
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500377
Wink Saville4c426ef2015-06-03 08:05:17 -0700378 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500379 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700380 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500381 try:
382 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800383 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800384 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500385 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700386 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700387 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500388 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700389 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500390 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700391 _error('Cannot link file %s to %s', relSrc, absDest)
392
393 def _Link(self):
Mike Frysingere6a202f2019-08-02 15:57:57 -0400394 """Link the self.src & self.dest paths.
395
396 Handles wild cards on the src linking all of the files in the source in to
397 the destination directory.
Wink Saville4c426ef2015-06-03 08:05:17 -0700398 """
Mike Frysinger07392ed2020-02-10 21:35:48 -0500399 # Some people use src="." to create stable links to projects. Lets allow
400 # that but reject all other uses of "." to keep things simple.
401 if self.src == '.':
402 src = self.git_worktree
403 else:
404 src = _SafeExpandPath(self.git_worktree, self.src)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400405
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300406 if not glob.has_magic(src):
407 # Entity does not contain a wild card so just a simple one to one link operation.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400408 dest = _SafeExpandPath(self.topdir, self.dest, skipfinal=True)
409 # dest & src are absolute paths at this point. Make sure the target of
410 # the symlink is relative in the context of the repo client checkout.
411 relpath = os.path.relpath(src, os.path.dirname(dest))
412 self.__linkIt(relpath, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700413 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400414 dest = _SafeExpandPath(self.topdir, self.dest)
Angel Petkovdbfbcb12020-05-02 23:16:20 +0300415 # Entity contains a wild card.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400416 if os.path.exists(dest) and not platform_utils.isdir(dest):
417 _error('Link error: src with wildcard, %s must be a directory', dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700418 else:
Mike Frysingere6a202f2019-08-02 15:57:57 -0400419 for absSrcFile in glob.glob(src):
Wink Saville4c426ef2015-06-03 08:05:17 -0700420 # Create a releative path from source dir to destination dir
421 absSrcDir = os.path.dirname(absSrcFile)
Mike Frysingere6a202f2019-08-02 15:57:57 -0400422 relSrcDir = os.path.relpath(absSrcDir, dest)
Wink Saville4c426ef2015-06-03 08:05:17 -0700423
424 # Get the source file name
425 srcFile = os.path.basename(absSrcFile)
426
427 # Now form the final full paths to srcFile. They will be
428 # absolute for the desintaiton and relative for the srouce.
Mike Frysingere6a202f2019-08-02 15:57:57 -0400429 absDest = os.path.join(dest, srcFile)
Wink Saville4c426ef2015-06-03 08:05:17 -0700430 relSrc = os.path.join(relSrcDir, srcFile)
431 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500432
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700433
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700434class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700435
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700436 def __init__(self,
437 name,
Anthony King7bdac712014-07-16 12:56:40 +0100438 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700439 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100440 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700441 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700442 orig_name=None,
443 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700444 self.name = name
445 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700446 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700447 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100448 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700449 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700450 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700451
452class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600453 # These objects can be shared between several working trees.
454 shareable_files = ['description', 'info']
455 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
456 # These objects can only be used by a single working tree.
457 working_tree_files = ['config', 'packed-refs', 'shallow']
458 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700459
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700460 def __init__(self,
461 manifest,
462 name,
463 remote,
464 gitdir,
David James8d201162013-10-11 17:03:19 -0700465 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700466 worktree,
467 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700468 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800469 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100470 rebase=True,
471 groups=None,
472 sync_c=False,
473 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900474 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100475 clone_depth=None,
476 upstream=None,
477 parent=None,
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500478 use_git_worktrees=False,
Anthony King7bdac712014-07-16 12:56:40 +0100479 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900480 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700481 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600482 retry_fetches=0,
Simran Basib9a1b732015-08-20 12:19:28 -0700483 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800484 """Init a Project object.
485
486 Args:
487 manifest: The XmlManifest object.
488 name: The `name` attribute of manifest.xml's project element.
489 remote: RemoteSpec object specifying its remote's properties.
490 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700491 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800492 worktree: Absolute path of git working tree.
493 relpath: Relative path of git working tree to repo's top directory.
494 revisionExpr: The `revision` attribute of manifest.xml's project element.
495 revisionId: git commit id for checking out.
496 rebase: The `rebase` attribute of manifest.xml's project element.
497 groups: The `groups` attribute of manifest.xml's project element.
498 sync_c: The `sync-c` attribute of manifest.xml's project element.
499 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900500 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800501 upstream: The `upstream` attribute of manifest.xml's project element.
502 parent: The parent Project object.
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500503 use_git_worktrees: Whether to use `git worktree` for this project.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800504 is_derived: False if the project was explicitly defined in the manifest;
505 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400506 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900507 optimized_fetch: If True, when a project is set to a sha1 revision, only
508 fetch from the remote if the sha1 is not present locally.
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600509 retry_fetches: Retry remote fetches n times upon receiving transient error
510 with exponential backoff and jitter.
Simran Basib9a1b732015-08-20 12:19:28 -0700511 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800512 """
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400513 self.client = self.manifest = manifest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700514 self.name = name
515 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800516 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700517 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800518 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700519 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800520 else:
521 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700522 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700523 self.revisionExpr = revisionExpr
524
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700525 if revisionId is None \
526 and revisionExpr \
527 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700528 self.revisionId = revisionExpr
529 else:
530 self.revisionId = revisionId
531
Mike Pontillod3153822012-02-28 11:53:24 -0800532 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700533 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700534 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800535 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900536 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900537 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700538 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800539 self.parent = parent
Mike Frysinger979d5bd2020-02-09 02:28:34 -0500540 # NB: Do not use this setting in __init__ to change behavior so that the
541 # manifest.git checkout can inspect & change it after instantiating. See
542 # the XmlManifest init code for more info.
543 self.use_git_worktrees = use_git_worktrees
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800544 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900545 self.optimized_fetch = optimized_fetch
George Engelbrecht9bc283e2020-04-02 12:36:09 -0600546 self.retry_fetches = max(0, retry_fetches)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800547 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800548
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700549 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700550 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500551 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500552 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700553 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -0400554 defaults=self.client.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700555
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800556 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700557 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800558 else:
559 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700560 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700561 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700562 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400563 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700564 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700565
Doug Anderson37282b42011-03-04 11:54:18 -0800566 # This will be filled in if a project is later identified to be the
567 # project containing repo hooks.
568 self.enabled_repo_hooks = []
569
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700570 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800571 def Derived(self):
572 return self.is_derived
573
574 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700575 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700576 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700577
578 @property
579 def CurrentBranch(self):
580 """Obtain the name of the currently checked out branch.
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400581
582 The branch name omits the 'refs/heads/' prefix.
583 None is returned if the project is on a detached HEAD, or if the work_git is
584 otheriwse inaccessible (e.g. an incomplete sync).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700585 """
Mike Frysingerc8290ad2019-10-01 01:07:11 -0400586 try:
587 b = self.work_git.GetHead()
588 except NoManifestException:
589 # If the local checkout is in a bad state, don't barf. Let the callers
590 # process this like the head is unreadable.
591 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700592 if b.startswith(R_HEADS):
593 return b[len(R_HEADS):]
594 return None
595
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700596 def IsRebaseInProgress(self):
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -0500597 return (os.path.exists(self.work_git.GetDotgitPath('rebase-apply')) or
598 os.path.exists(self.work_git.GetDotgitPath('rebase-merge')) or
599 os.path.exists(os.path.join(self.worktree, '.dotest')))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200600
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700601 def IsDirty(self, consider_untracked=True):
602 """Is the working directory modified in some way?
603 """
604 self.work_git.update_index('-q',
605 '--unmerged',
606 '--ignore-missing',
607 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900608 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700609 return True
610 if self.work_git.DiffZ('diff-files'):
611 return True
612 if consider_untracked and self.work_git.LsOthers():
613 return True
614 return False
615
616 _userident_name = None
617 _userident_email = None
618
619 @property
620 def UserName(self):
621 """Obtain the user's personal name.
622 """
623 if self._userident_name is None:
624 self._LoadUserIdentity()
625 return self._userident_name
626
627 @property
628 def UserEmail(self):
629 """Obtain the user's email address. This is very likely
630 to be their Gerrit login.
631 """
632 if self._userident_email is None:
633 self._LoadUserIdentity()
634 return self._userident_email
635
636 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900637 u = self.bare_git.var('GIT_COMMITTER_IDENT')
638 m = re.compile("^(.*) <([^>]*)> ").match(u)
639 if m:
640 self._userident_name = m.group(1)
641 self._userident_email = m.group(2)
642 else:
643 self._userident_name = ''
644 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700645
646 def GetRemote(self, name):
647 """Get the configuration for a single remote.
648 """
649 return self.config.GetRemote(name)
650
651 def GetBranch(self, name):
652 """Get the configuration for a single branch.
653 """
654 return self.config.GetBranch(name)
655
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700656 def GetBranches(self):
657 """Get all existing local branches.
658 """
659 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900660 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700661 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700662
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530663 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700664 if name.startswith(R_HEADS):
665 name = name[len(R_HEADS):]
666 b = self.GetBranch(name)
667 b.current = name == current
668 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900669 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700670 heads[name] = b
671
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530672 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700673 if name.startswith(R_PUB):
674 name = name[len(R_PUB):]
675 b = heads.get(name)
676 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900677 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700678
679 return heads
680
Colin Cross5acde752012-03-28 20:15:45 -0700681 def MatchesGroups(self, manifest_groups):
682 """Returns true if the manifest groups specified at init should cause
683 this project to be synced.
684 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700685 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700686
687 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700688 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700689 manifest_groups: "-group1,group2"
690 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500691
692 The special manifest group "default" will match any project that
693 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700694 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500695 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700696 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700697 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500698 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700699
Conley Owens971de8e2012-04-16 10:36:08 -0700700 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700701 for group in expanded_manifest_groups:
702 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700703 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700704 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700705 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700706
Conley Owens971de8e2012-04-16 10:36:08 -0700707 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700708
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700709# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700710 def UncommitedFiles(self, get_all=True):
711 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700712
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700713 Args:
714 get_all: a boolean, if True - get information about all different
715 uncommitted files. If False - return as soon as any kind of
716 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500717 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700718 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500719 self.work_git.update_index('-q',
720 '--unmerged',
721 '--ignore-missing',
722 '--refresh')
723 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700724 details.append("rebase in progress")
725 if not get_all:
726 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500727
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700728 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
729 if changes:
730 details.extend(changes)
731 if not get_all:
732 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500733
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700734 changes = self.work_git.DiffZ('diff-files').keys()
735 if changes:
736 details.extend(changes)
737 if not get_all:
738 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500739
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700740 changes = self.work_git.LsOthers()
741 if changes:
742 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500743
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700744 return details
745
746 def HasChanges(self):
747 """Returns true if there are uncommitted changes.
748 """
749 if self.UncommitedFiles(get_all=False):
750 return True
751 else:
752 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500753
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600754 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700755 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200756
757 Args:
Rostislav Krasny0bcc2d22020-01-25 14:49:14 +0200758 output_redir: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600759 quiet: If True then only print the project name. Do not print
760 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700761 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700762 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700763 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200764 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700765 print(file=output_redir)
766 print('project %s/' % self.relpath, file=output_redir)
767 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700768 return
769
770 self.work_git.update_index('-q',
771 '--unmerged',
772 '--ignore-missing',
773 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700774 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
776 df = self.work_git.DiffZ('diff-files')
777 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100778 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700779 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780
781 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700782 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200783 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700784 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700785
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600786 if quiet:
787 out.nl()
788 return 'DIRTY'
789
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700790 branch = self.CurrentBranch
791 if branch is None:
792 out.nobranch('(*** NO BRANCH ***)')
793 else:
794 out.branch('branch %s', branch)
795 out.nl()
796
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700797 if rb:
798 out.important('prior sync failed; rebase still in progress')
799 out.nl()
800
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700801 paths = list()
802 paths.extend(di.keys())
803 paths.extend(df.keys())
804 paths.extend(do)
805
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530806 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900807 try:
808 i = di[p]
809 except KeyError:
810 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700811
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900812 try:
813 f = df[p]
814 except KeyError:
815 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200816
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900817 if i:
818 i_status = i.status.upper()
819 else:
820 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700821
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900822 if f:
823 f_status = f.status.lower()
824 else:
825 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700826
827 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800828 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700829 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700830 else:
831 line = ' %s%s\t%s' % (i_status, f_status, p)
832
833 if i and not f:
834 out.added('%s', line)
835 elif (i and f) or (not i and f):
836 out.changed('%s', line)
837 elif not i and not f:
838 out.untracked('%s', line)
839 else:
840 out.write('%s', line)
841 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +0200842
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700843 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700844
pelyad67872d2012-03-28 14:49:58 +0300845 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700846 """Prints the status of the repository to stdout.
847 """
848 out = DiffColoring(self.config)
849 cmd = ['diff']
850 if out.is_on:
851 cmd.append('--color')
852 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +0300853 if absolute_paths:
854 cmd.append('--src-prefix=a/%s/' % self.relpath)
855 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700856 cmd.append('--')
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400857 try:
858 p = GitCommand(self,
859 cmd,
860 capture_stdout=True,
861 capture_stderr=True)
862 except GitError as e:
863 out.nl()
864 out.project('project %s/' % self.relpath)
865 out.nl()
866 out.fail('%s', str(e))
867 out.nl()
868 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700869 has_diff = False
870 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -0400871 if not hasattr(line, 'encode'):
872 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700873 if not has_diff:
874 out.nl()
875 out.project('project %s/' % self.relpath)
876 out.nl()
877 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -0700878 print(line[:-1])
Mike Frysinger0a9265e2019-09-30 23:59:27 -0400879 return p.Wait() == 0
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700881# Publish / Upload ##
David Pursehouse8a68ff92012-09-24 12:15:13 +0900882 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883 """Was the branch published (uploaded) for code review?
884 If so, returns the SHA-1 hash of the last published
885 state for the branch.
886 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700887 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900888 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700889 try:
890 return self.bare_git.rev_parse(key)
891 except GitError:
892 return None
893 else:
894 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900895 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -0700896 except KeyError:
897 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700898
David Pursehouse8a68ff92012-09-24 12:15:13 +0900899 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700900 """Prunes any stale published refs.
901 """
David Pursehouse8a68ff92012-09-24 12:15:13 +0900902 if all_refs is None:
903 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700904 heads = set()
905 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530906 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700907 if name.startswith(R_HEADS):
908 heads.add(name)
909 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900910 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700911
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530912 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700913 n = name[len(R_PUB):]
914 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900915 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700916
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700917 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700918 """List any branches which can be uploaded for review.
919 """
920 heads = {}
921 pubed = {}
922
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530923 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700924 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900925 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +0900927 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928
929 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530930 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +0900931 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -0700933 if selected_branch and branch != selected_branch:
934 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800936 rb = self.GetUploadableBranch(branch)
937 if rb:
938 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939 return ready
940
Shawn O. Pearce35f25962008-11-11 17:03:13 -0800941 def GetUploadableBranch(self, branch_name):
942 """Get a single uploadable branch, or None.
943 """
944 branch = self.GetBranch(branch_name)
945 base = branch.LocalMerge
946 if branch.LocalMerge:
947 rb = ReviewableBranch(self, branch, base)
948 if rb.commits:
949 return rb
950 return None
951
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700952 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +0100953 people=([], []),
Mike Frysinger819cc812020-02-19 02:27:22 -0500954 dryrun=False,
Brian Harring435370c2012-07-28 15:37:04 -0700955 auto_topic=False,
Mike Frysinger84685ba2020-02-19 02:22:22 -0500956 hashtags=(),
Mike Frysingerfc1b18a2020-02-24 15:38:07 -0500957 labels=(),
Changcheng Xiao87984c62017-08-02 16:55:03 +0200958 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700959 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200960 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200961 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800962 validate_certs=True,
963 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 """Uploads the named branch for code review.
965 """
966 if branch is None:
967 branch = self.CurrentBranch
968 if branch is None:
969 raise GitError('not currently on a branch')
970
971 branch = self.GetBranch(branch)
972 if not branch.LocalMerge:
973 raise GitError('branch %s does not track a remote' % branch.name)
974 if not branch.remote.review:
975 raise GitError('remote %s has no review url' % branch.remote.name)
976
Bryan Jacobsf609f912013-05-06 13:36:24 -0400977 if dest_branch is None:
978 dest_branch = self.dest_branch
979 if dest_branch is None:
980 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700981 if not dest_branch.startswith(R_HEADS):
982 dest_branch = R_HEADS + dest_branch
983
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -0800984 if not branch.remote.projectname:
985 branch.remote.projectname = self.name
986 branch.remote.Save()
987
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200988 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800989 if url is None:
990 raise UploadError('review not configured')
991 cmd = ['push']
Mike Frysinger819cc812020-02-19 02:27:22 -0500992 if dryrun:
993 cmd.append('-n')
Shawn O. Pearceb54a3922009-01-05 16:18:58 -0800994
Shawn O. Pearcec9571422012-01-11 14:58:54 -0800995 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -0800996 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700997
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800998 for push_option in (push_options or []):
999 cmd.append('-o')
1000 cmd.append(push_option)
1001
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001002 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001003
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001004 if dest_branch.startswith(R_HEADS):
1005 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001006
Mike Frysingerb0fbc7f2020-02-25 02:58:04 -05001007 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001008 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001009 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001010 opts += ['topic=' + branch.name]
Mike Frysinger84685ba2020-02-19 02:22:22 -05001011 opts += ['t=%s' % p for p in hashtags]
Mike Frysingerfc1b18a2020-02-24 15:38:07 -05001012 opts += ['l=%s' % p for p in labels]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001013
David Pursehousef25a3702018-11-14 19:01:22 -08001014 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001015 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001016 if notify:
1017 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001018 if private:
1019 opts += ['private']
1020 if wip:
1021 opts += ['wip']
1022 if opts:
1023 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001024 cmd.append(ref_spec)
1025
Anthony King7bdac712014-07-16 12:56:40 +01001026 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001027 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028
Mike Frysingerd7f86832020-11-19 19:18:46 -05001029 if not dryrun:
1030 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1031 self.bare_git.UpdateRef(R_PUB + branch.name,
1032 R_HEADS + branch.name,
1033 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001034
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001035# Sync ##
Julien Campergue335f5ef2013-10-16 11:02:35 +02001036 def _ExtractArchive(self, tarpath, path=None):
1037 """Extract the given tar on its current location
1038
1039 Args:
1040 - tarpath: The path to the actual tar file
1041
1042 """
1043 try:
1044 with tarfile.open(tarpath, 'r') as tar:
1045 tar.extractall(path=path)
1046 return True
1047 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001048 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001049 return False
1050
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001051 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001052 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001053 verbose=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001054 is_new=None,
1055 current_branch_only=False,
1056 force_sync=False,
1057 clone_bundle=True,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001058 tags=True,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001059 archive=False,
1060 optimized_fetch=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001061 retry_fetches=0,
Martin Kellye4e94d22017-03-21 16:05:12 -07001062 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001063 submodules=False,
1064 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 """Perform only the network IO portion of the sync process.
1066 Local working directory/branch state is not affected.
1067 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001068 if archive and not isinstance(self, MetaProject):
1069 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001070 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001071 return False
1072
1073 name = self.relpath.replace('\\', '/')
1074 name = name.replace('/', '_')
1075 tarpath = '%s.tar' % name
1076 topdir = self.manifest.topdir
1077
1078 try:
1079 self._FetchArchive(tarpath, cwd=topdir)
1080 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001081 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001082 return False
1083
1084 # From now on, we only need absolute tarpath
1085 tarpath = os.path.join(topdir, tarpath)
1086
1087 if not self._ExtractArchive(tarpath, path=topdir):
1088 return False
1089 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001090 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001091 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001092 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001093 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001094 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001095 if is_new is None:
1096 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001097 if is_new:
David Pursehousee8ace262020-02-13 12:41:15 +09001098 self._InitGitDir(force_sync=force_sync, quiet=quiet)
Jimmie Westera0444582012-10-24 13:44:42 +02001099 else:
David Pursehousee8ace262020-02-13 12:41:15 +09001100 self._UpdateHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001102
1103 if is_new:
1104 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1105 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05001106 with open(alt) as fd:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001107 # This works for both absolute and relative alternate directories.
1108 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001109 except IOError:
1110 alt_dir = None
1111 else:
1112 alt_dir = None
1113
Mike Frysingere50b6a72020-02-19 01:45:48 -05001114 if (clone_bundle
1115 and alt_dir is None
1116 and self._ApplyCloneBundle(initial=is_new, quiet=quiet, verbose=verbose)):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001117 is_new = False
1118
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001119 if not current_branch_only:
1120 if self.sync_c:
1121 current_branch_only = True
1122 elif not self.manifest._loaded:
1123 # Manifest cannot check defaults until it syncs.
1124 current_branch_only = False
1125 elif self.manifest.default.sync_c:
1126 current_branch_only = True
1127
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001128 if not self.sync_tags:
1129 tags = False
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001130
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001131 if self.clone_depth:
1132 depth = self.clone_depth
1133 else:
1134 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1135
Mike Frysinger521d01b2020-02-17 01:51:49 -05001136 # See if we can skip the network fetch entirely.
1137 if not (optimized_fetch and
1138 (ID_RE.match(self.revisionExpr) and
1139 self._CheckForImmutableRevision())):
1140 if not self._RemoteFetch(
David Pursehouse3cceda52020-02-18 14:11:39 +09001141 initial=is_new, quiet=quiet, verbose=verbose, alt_dir=alt_dir,
1142 current_branch_only=current_branch_only,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001143 tags=tags, prune=prune, depth=depth,
David Pursehouse3cceda52020-02-18 14:11:39 +09001144 submodules=submodules, force_sync=force_sync,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001145 clone_filter=clone_filter, retry_fetches=retry_fetches):
Mike Frysinger521d01b2020-02-17 01:51:49 -05001146 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001147
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001148 mp = self.manifest.manifestProject
1149 dissociate = mp.config.GetBoolean('repo.dissociate')
1150 if dissociate:
1151 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1152 if os.path.exists(alternates_file):
1153 cmd = ['repack', '-a', '-d']
1154 if GitCommand(self, cmd, bare=True).Wait() != 0:
1155 return False
1156 platform_utils.remove(alternates_file)
1157
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001158 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001159 self._InitMRef()
1160 else:
1161 self._InitMirrorHead()
1162 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001163 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001164 except OSError:
1165 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001166 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001167
1168 def PostRepoUpgrade(self):
1169 self._InitHooks()
1170
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001171 def _CopyAndLinkFiles(self):
Mike Frysinger8c1e9cb2020-09-06 14:53:18 -04001172 if self.client.isGitcClient:
Simran Basib9a1b732015-08-20 12:19:28 -07001173 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001174 for copyfile in self.copyfiles:
1175 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001176 for linkfile in self.linkfiles:
1177 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001178
Julien Camperguedd654222014-01-09 16:21:37 +01001179 def GetCommitRevisionId(self):
1180 """Get revisionId of a commit.
1181
1182 Use this method instead of GetRevisionId to get the id of the commit rather
1183 than the id of the current git object (for example, a tag)
1184
1185 """
1186 if not self.revisionExpr.startswith(R_TAGS):
1187 return self.GetRevisionId(self._allrefs)
1188
1189 try:
1190 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1191 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001192 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1193 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001194
David Pursehouse8a68ff92012-09-24 12:15:13 +09001195 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001196 if self.revisionId:
1197 return self.revisionId
1198
1199 rem = self.GetRemote(self.remote.name)
1200 rev = rem.ToLocal(self.revisionExpr)
1201
David Pursehouse8a68ff92012-09-24 12:15:13 +09001202 if all_refs is not None and rev in all_refs:
1203 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001204
1205 try:
1206 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1207 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001208 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1209 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001210
Martin Kellye4e94d22017-03-21 16:05:12 -07001211 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001212 """Perform only the local IO portion of the sync process.
1213 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001214 """
Mike Frysingerb610b852019-11-11 05:10:03 -05001215 if not os.path.exists(self.gitdir):
1216 syncbuf.fail(self,
1217 'Cannot checkout %s due to missing network sync; Run '
1218 '`repo sync -n %s` first.' %
1219 (self.name, self.name))
1220 return
1221
Martin Kellye4e94d22017-03-21 16:05:12 -07001222 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001223 all_refs = self.bare_ref.all
1224 self.CleanPublishedCache(all_refs)
1225 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001226
David Pursehouse1d947b32012-10-25 12:23:11 +09001227 def _doff():
1228 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001229 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001230
Martin Kellye4e94d22017-03-21 16:05:12 -07001231 def _dosubmodules():
1232 self._SyncSubmodules(quiet=True)
1233
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001234 head = self.work_git.GetHead()
1235 if head.startswith(R_HEADS):
1236 branch = head[len(R_HEADS):]
1237 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001238 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001239 except KeyError:
1240 head = None
1241 else:
1242 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001243
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001244 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001245 # Currently on a detached HEAD. The user is assumed to
1246 # not have any local modifications worth worrying about.
1247 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001248 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001249 syncbuf.fail(self, _PriorSyncFailedError())
1250 return
1251
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001252 if head == revid:
1253 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001254 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001255 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001256 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001257 # The copy/linkfile config may have changed.
1258 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001259 return
1260 else:
1261 lost = self._revlist(not_rev(revid), HEAD)
1262 if lost:
1263 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001264
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001265 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001266 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001267 if submodules:
1268 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001269 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001270 syncbuf.fail(self, e)
1271 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001272 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001273 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001274
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001275 if head == revid:
1276 # No changes; don't do anything further.
1277 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001278 # The copy/linkfile config may have changed.
1279 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001280 return
1281
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001282 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001283
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001284 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001285 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001286 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001287 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001288 syncbuf.info(self,
1289 "leaving %s; does not track upstream",
1290 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001292 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001293 if submodules:
1294 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001295 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001296 syncbuf.fail(self, e)
1297 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001298 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001299 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001300
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001301 upstream_gain = self._revlist(not_rev(HEAD), revid)
Mike Frysinger2ba5a1e2019-09-23 17:42:23 -04001302
1303 # See if we can perform a fast forward merge. This can happen if our
1304 # branch isn't in the exact same state as we last published.
1305 try:
1306 self.work_git.merge_base('--is-ancestor', HEAD, revid)
1307 # Skip the published logic.
1308 pub = False
1309 except GitError:
1310 pub = self.WasPublished(branch.name, all_refs)
1311
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001312 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001313 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001314 if not_merged:
1315 if upstream_gain:
1316 # The user has published this branch and some of those
1317 # commits are not yet merged upstream. We do not want
1318 # to rewrite the published commits so we punt.
1319 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001320 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001321 "branch %s is published (but not merged) and is now "
1322 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001323 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001324 elif pub == head:
1325 # All published commits are merged, and thus we are a
1326 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001327 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001328 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001329 if submodules:
1330 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001331 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001332
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001333 # Examine the local commits not in the remote. Find the
1334 # last one attributed to this user, if any.
1335 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001336 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001337 last_mine = None
1338 cnt_mine = 0
1339 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001340 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001341 if committer_email == self.UserEmail:
1342 last_mine = commit_id
1343 cnt_mine += 1
1344
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001345 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001346 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001347
1348 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001349 syncbuf.fail(self, _DirtyError())
1350 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001351
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001352 # If the upstream switched on us, warn the user.
1353 #
1354 if branch.merge != self.revisionExpr:
1355 if branch.merge and self.revisionExpr:
1356 syncbuf.info(self,
1357 'manifest switched %s...%s',
1358 branch.merge,
1359 self.revisionExpr)
1360 elif branch.merge:
1361 syncbuf.info(self,
1362 'manifest no longer tracks %s',
1363 branch.merge)
1364
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001365 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001366 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001367 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001368 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001369 syncbuf.info(self,
1370 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001371 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001372
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001373 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001374 if not ID_RE.match(self.revisionExpr):
1375 # in case of manifest sync the revisionExpr might be a SHA1
1376 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001377 if not branch.merge.startswith('refs/'):
1378 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001379 branch.Save()
1380
Mike Pontillod3153822012-02-28 11:53:24 -08001381 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001382 def _docopyandlink():
1383 self._CopyAndLinkFiles()
1384
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001385 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001386 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001387 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001388 if submodules:
1389 syncbuf.later2(self, _dosubmodules)
1390 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001391 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001392 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001393 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001394 if submodules:
1395 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001396 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001397 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001398 syncbuf.fail(self, e)
1399 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001400 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001401 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001402 if submodules:
1403 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001404
Mike Frysingere6a202f2019-08-02 15:57:57 -04001405 def AddCopyFile(self, src, dest, topdir):
1406 """Mark |src| for copying to |dest| (relative to |topdir|).
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001407
Mike Frysingere6a202f2019-08-02 15:57:57 -04001408 No filesystem changes occur here. Actual copying happens later on.
1409
1410 Paths should have basic validation run on them before being queued.
1411 Further checking will be handled when the actual copy happens.
1412 """
1413 self.copyfiles.append(_CopyFile(self.worktree, src, topdir, dest))
1414
1415 def AddLinkFile(self, src, dest, topdir):
1416 """Mark |dest| to create a symlink (relative to |topdir|) pointing to |src|.
1417
1418 No filesystem changes occur here. Actual linking happens later on.
1419
1420 Paths should have basic validation run on them before being queued.
1421 Further checking will be handled when the actual link happens.
1422 """
1423 self.linkfiles.append(_LinkFile(self.worktree, src, topdir, dest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001424
James W. Mills24c13082012-04-12 15:04:13 -05001425 def AddAnnotation(self, name, value, keep):
1426 self.annotations.append(_Annotation(name, value, keep))
1427
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001428 def DownloadPatchSet(self, change_id, patch_id):
1429 """Download a single patch set of a single change to FETCH_HEAD.
1430 """
1431 remote = self.GetRemote(self.remote.name)
1432
1433 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001434 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001435 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001436 if GitCommand(self, cmd, bare=True).Wait() != 0:
1437 return None
1438 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001439 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001440 change_id,
1441 patch_id,
1442 self.bare_git.rev_parse('FETCH_HEAD'))
1443
Mike Frysingerc0d18662020-02-19 19:19:18 -05001444 def DeleteWorktree(self, quiet=False, force=False):
1445 """Delete the source checkout and any other housekeeping tasks.
1446
1447 This currently leaves behind the internal .repo/ cache state. This helps
1448 when switching branches or manifest changes get reverted as we don't have
1449 to redownload all the git objects. But we should do some GC at some point.
1450
1451 Args:
1452 quiet: Whether to hide normal messages.
1453 force: Always delete tree even if dirty.
1454
1455 Returns:
1456 True if the worktree was completely cleaned out.
1457 """
1458 if self.IsDirty():
1459 if force:
1460 print('warning: %s: Removing dirty project: uncommitted changes lost.' %
1461 (self.relpath,), file=sys.stderr)
1462 else:
1463 print('error: %s: Cannot remove project: uncommitted changes are '
1464 'present.\n' % (self.relpath,), file=sys.stderr)
1465 return False
1466
1467 if not quiet:
1468 print('%s: Deleting obsolete checkout.' % (self.relpath,))
1469
1470 # Unlock and delink from the main worktree. We don't use git's worktree
1471 # remove because it will recursively delete projects -- we handle that
1472 # ourselves below. https://crbug.com/git/48
1473 if self.use_git_worktrees:
1474 needle = platform_utils.realpath(self.gitdir)
1475 # Find the git worktree commondir under .repo/worktrees/.
1476 output = self.bare_git.worktree('list', '--porcelain').splitlines()[0]
1477 assert output.startswith('worktree '), output
1478 commondir = output[9:]
1479 # Walk each of the git worktrees to see where they point.
1480 configs = os.path.join(commondir, 'worktrees')
1481 for name in os.listdir(configs):
1482 gitdir = os.path.join(configs, name, 'gitdir')
1483 with open(gitdir) as fp:
1484 relpath = fp.read().strip()
1485 # Resolve the checkout path and see if it matches this project.
1486 fullpath = platform_utils.realpath(os.path.join(configs, name, relpath))
1487 if fullpath == needle:
1488 platform_utils.rmtree(os.path.join(configs, name))
1489
1490 # Delete the .git directory first, so we're less likely to have a partially
1491 # working git repository around. There shouldn't be any git projects here,
1492 # so rmtree works.
1493
1494 # Try to remove plain files first in case of git worktrees. If this fails
1495 # for any reason, we'll fall back to rmtree, and that'll display errors if
1496 # it can't remove things either.
1497 try:
1498 platform_utils.remove(self.gitdir)
1499 except OSError:
1500 pass
1501 try:
1502 platform_utils.rmtree(self.gitdir)
1503 except OSError as e:
1504 if e.errno != errno.ENOENT:
1505 print('error: %s: %s' % (self.gitdir, e), file=sys.stderr)
1506 print('error: %s: Failed to delete obsolete checkout; remove manually, '
1507 'then run `repo sync -l`.' % (self.relpath,), file=sys.stderr)
1508 return False
1509
1510 # Delete everything under the worktree, except for directories that contain
1511 # another git project.
1512 dirs_to_remove = []
1513 failed = False
1514 for root, dirs, files in platform_utils.walk(self.worktree):
1515 for f in files:
1516 path = os.path.join(root, f)
1517 try:
1518 platform_utils.remove(path)
1519 except OSError as e:
1520 if e.errno != errno.ENOENT:
1521 print('error: %s: Failed to remove: %s' % (path, e), file=sys.stderr)
1522 failed = True
1523 dirs[:] = [d for d in dirs
1524 if not os.path.lexists(os.path.join(root, d, '.git'))]
1525 dirs_to_remove += [os.path.join(root, d) for d in dirs
1526 if os.path.join(root, d) not in dirs_to_remove]
1527 for d in reversed(dirs_to_remove):
1528 if platform_utils.islink(d):
1529 try:
1530 platform_utils.remove(d)
1531 except OSError as e:
1532 if e.errno != errno.ENOENT:
1533 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1534 failed = True
1535 elif not platform_utils.listdir(d):
1536 try:
1537 platform_utils.rmdir(d)
1538 except OSError as e:
1539 if e.errno != errno.ENOENT:
1540 print('error: %s: Failed to remove: %s' % (d, e), file=sys.stderr)
1541 failed = True
1542 if failed:
1543 print('error: %s: Failed to delete obsolete checkout.' % (self.relpath,),
1544 file=sys.stderr)
1545 print(' Remove manually, then run `repo sync -l`.', file=sys.stderr)
1546 return False
1547
1548 # Try deleting parent dirs if they are empty.
1549 path = self.worktree
1550 while path != self.manifest.topdir:
1551 try:
1552 platform_utils.rmdir(path)
1553 except OSError as e:
1554 if e.errno != errno.ENOENT:
1555 break
1556 path = os.path.dirname(path)
1557
1558 return True
1559
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001560# Branch Management ##
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001561 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562 """Create a new branch off the manifest's revision.
1563 """
Simran Basib9a1b732015-08-20 12:19:28 -07001564 if not branch_merge:
1565 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001566 head = self.work_git.GetHead()
1567 if head == (R_HEADS + name):
1568 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001569
David Pursehouse8a68ff92012-09-24 12:15:13 +09001570 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001571 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001572 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001573 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001574 capture_stdout=True,
1575 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001576
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001577 branch = self.GetBranch(name)
1578 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001579 branch.merge = branch_merge
1580 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1581 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001582
1583 if revision is None:
1584 revid = self.GetRevisionId(all_refs)
1585 else:
1586 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001587
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001588 if head.startswith(R_HEADS):
1589 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001590 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001591 except KeyError:
1592 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001593 if revid and head and revid == head:
Mike Frysinger746e7f62020-02-19 15:49:02 -05001594 ref = R_HEADS + name
1595 self.work_git.update_ref(ref, revid)
1596 self.work_git.symbolic_ref(HEAD, ref)
1597 branch.Save()
1598 return True
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001599
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001600 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001601 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001602 capture_stdout=True,
1603 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001604 branch.Save()
1605 return True
1606 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001607
Wink Saville02d79452009-04-10 13:01:24 -07001608 def CheckoutBranch(self, name):
1609 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001610
1611 Args:
1612 name: The name of the branch to checkout.
1613
1614 Returns:
1615 True if the checkout succeeded; False if it didn't; None if the branch
1616 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001617 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001618 rev = R_HEADS + name
1619 head = self.work_git.GetHead()
1620 if head == rev:
1621 # Already on the branch
1622 #
1623 return True
Wink Saville02d79452009-04-10 13:01:24 -07001624
David Pursehouse8a68ff92012-09-24 12:15:13 +09001625 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001626 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001627 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001628 except KeyError:
1629 # Branch does not exist in this project
1630 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001631 return None
Wink Saville02d79452009-04-10 13:01:24 -07001632
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001633 if head.startswith(R_HEADS):
1634 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001635 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001636 except KeyError:
1637 head = None
1638
1639 if head == revid:
1640 # Same revision; just update HEAD to point to the new
1641 # target branch, but otherwise take no other action.
1642 #
Mike Frysinger9f91c432020-02-23 23:24:40 -05001643 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD),
1644 'ref: %s%s\n' % (R_HEADS, name))
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001645 return True
1646
1647 return GitCommand(self,
1648 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001649 capture_stdout=True,
1650 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001651
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001652 def AbandonBranch(self, name):
1653 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001654
1655 Args:
1656 name: The name of the branch to abandon.
1657
1658 Returns:
1659 True if the abandon succeeded; False if it didn't; None if the branch
1660 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001661 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001662 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001663 all_refs = self.bare_ref.all
1664 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001665 # Doesn't exist
1666 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001667
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001668 head = self.work_git.GetHead()
1669 if head == rev:
1670 # We can't destroy the branch while we are sitting
1671 # on it. Switch to a detached HEAD.
1672 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001673 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001674
David Pursehouse8a68ff92012-09-24 12:15:13 +09001675 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001676 if head == revid:
Mike Frysinger9f91c432020-02-23 23:24:40 -05001677 _lwrite(self.work_git.GetDotgitPath(subpath=HEAD), '%s\n' % revid)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001678 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001679 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001680
1681 return GitCommand(self,
1682 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001683 capture_stdout=True,
1684 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001685
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001686 def PruneHeads(self):
1687 """Prune any topic branches already merged into upstream.
1688 """
1689 cb = self.CurrentBranch
1690 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001691 left = self._allrefs
1692 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001693 if name.startswith(R_HEADS):
1694 name = name[len(R_HEADS):]
1695 if cb is None or name != cb:
1696 kill.append(name)
1697
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001698 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001699 if cb is not None \
1700 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001701 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001702 self.work_git.DetachHead(HEAD)
1703 kill.append(cb)
1704
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001705 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001706 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001707
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001708 try:
1709 self.bare_git.DetachHead(rev)
1710
1711 b = ['branch', '-d']
1712 b.extend(kill)
1713 b = GitCommand(self, b, bare=True,
1714 capture_stdout=True,
1715 capture_stderr=True)
1716 b.Wait()
1717 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001718 if ID_RE.match(old):
1719 self.bare_git.DetachHead(old)
1720 else:
1721 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001722 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001724 for branch in kill:
1725 if (R_HEADS + branch) not in left:
1726 self.CleanPublishedCache()
1727 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001728
1729 if cb and cb not in kill:
1730 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001731 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732
1733 kept = []
1734 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001735 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001736 branch = self.GetBranch(branch)
1737 base = branch.LocalMerge
1738 if not base:
1739 base = rev
1740 kept.append(ReviewableBranch(self, branch, base))
1741 return kept
1742
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001743# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001744 def GetRegisteredSubprojects(self):
1745 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001746
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001747 def rec(subprojects):
1748 if not subprojects:
1749 return
1750 result.extend(subprojects)
1751 for p in subprojects:
1752 rec(p.subprojects)
1753 rec(self.subprojects)
1754 return result
1755
1756 def _GetSubmodules(self):
1757 # Unfortunately we cannot call `git submodule status --recursive` here
1758 # because the working tree might not exist yet, and it cannot be used
1759 # without a working tree in its current implementation.
1760
1761 def get_submodules(gitdir, rev):
1762 # Parse .gitmodules for submodule sub_paths and sub_urls
1763 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1764 if not sub_paths:
1765 return []
1766 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1767 # revision of submodule repository
1768 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1769 submodules = []
1770 for sub_path, sub_url in zip(sub_paths, sub_urls):
1771 try:
1772 sub_rev = sub_revs[sub_path]
1773 except KeyError:
1774 # Ignore non-exist submodules
1775 continue
1776 submodules.append((sub_rev, sub_path, sub_url))
1777 return submodules
1778
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001779 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1780 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001781
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001782 def parse_gitmodules(gitdir, rev):
1783 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1784 try:
Anthony King7bdac712014-07-16 12:56:40 +01001785 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1786 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001787 except GitError:
1788 return [], []
1789 if p.Wait() != 0:
1790 return [], []
1791
1792 gitmodules_lines = []
1793 fd, temp_gitmodules_path = tempfile.mkstemp()
1794 try:
Mike Frysinger163d42e2020-02-11 03:35:24 -05001795 os.write(fd, p.stdout.encode('utf-8'))
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001796 os.close(fd)
1797 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001798 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1799 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001800 if p.Wait() != 0:
1801 return [], []
1802 gitmodules_lines = p.stdout.split('\n')
1803 except GitError:
1804 return [], []
1805 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001806 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001807
1808 names = set()
1809 paths = {}
1810 urls = {}
1811 for line in gitmodules_lines:
1812 if not line:
1813 continue
1814 m = re_path.match(line)
1815 if m:
1816 names.add(m.group(1))
1817 paths[m.group(1)] = m.group(2)
1818 continue
1819 m = re_url.match(line)
1820 if m:
1821 names.add(m.group(1))
1822 urls[m.group(1)] = m.group(2)
1823 continue
1824 names = sorted(names)
1825 return ([paths.get(name, '') for name in names],
1826 [urls.get(name, '') for name in names])
1827
1828 def git_ls_tree(gitdir, rev, paths):
1829 cmd = ['ls-tree', rev, '--']
1830 cmd.extend(paths)
1831 try:
Anthony King7bdac712014-07-16 12:56:40 +01001832 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1833 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001834 except GitError:
1835 return []
1836 if p.Wait() != 0:
1837 return []
1838 objects = {}
1839 for line in p.stdout.split('\n'):
1840 if not line.strip():
1841 continue
1842 object_rev, object_path = line.split()[2:4]
1843 objects[object_path] = object_rev
1844 return objects
1845
1846 try:
1847 rev = self.GetRevisionId()
1848 except GitError:
1849 return []
1850 return get_submodules(self.gitdir, rev)
1851
1852 def GetDerivedSubprojects(self):
1853 result = []
1854 if not self.Exists:
1855 # If git repo does not exist yet, querying its submodules will
1856 # mess up its states; so return here.
1857 return result
1858 for rev, path, url in self._GetSubmodules():
1859 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001860 relpath, worktree, gitdir, objdir = \
1861 self.manifest.GetSubprojectPaths(self, name, path)
1862 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001863 if project:
1864 result.extend(project.GetDerivedSubprojects())
1865 continue
David James8d201162013-10-11 17:03:19 -07001866
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001867 if url.startswith('..'):
1868 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001869 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001870 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001871 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001872 review=self.remote.review,
1873 revision=self.remote.revision)
1874 subproject = Project(manifest=self.manifest,
1875 name=name,
1876 remote=remote,
1877 gitdir=gitdir,
1878 objdir=objdir,
1879 worktree=worktree,
1880 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001881 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001882 revisionId=rev,
1883 rebase=self.rebase,
1884 groups=self.groups,
1885 sync_c=self.sync_c,
1886 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001887 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001888 parent=self,
1889 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001890 result.append(subproject)
1891 result.extend(subproject.GetDerivedSubprojects())
1892 return result
1893
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001894# Direct Git Commands ##
Mike Frysingerf81c72e2020-02-19 15:50:00 -05001895 def EnableRepositoryExtension(self, key, value='true', version=1):
1896 """Enable git repository extension |key| with |value|.
1897
1898 Args:
1899 key: The extension to enabled. Omit the "extensions." prefix.
1900 value: The value to use for the extension.
1901 version: The minimum git repository version needed.
1902 """
1903 # Make sure the git repo version is new enough already.
1904 found_version = self.config.GetInt('core.repositoryFormatVersion')
1905 if found_version is None:
1906 found_version = 0
1907 if found_version < version:
1908 self.config.SetString('core.repositoryFormatVersion', str(version))
1909
1910 # Enable the extension!
1911 self.config.SetString('extensions.%s' % (key,), value)
1912
Mike Frysinger50a81de2020-09-06 15:51:21 -04001913 def ResolveRemoteHead(self, name=None):
1914 """Find out what the default branch (HEAD) points to.
1915
1916 Normally this points to refs/heads/master, but projects are moving to main.
1917 Support whatever the server uses rather than hardcoding "master" ourselves.
1918 """
1919 if name is None:
1920 name = self.remote.name
1921
1922 # The output will look like (NB: tabs are separators):
1923 # ref: refs/heads/master HEAD
1924 # 5f6803b100bb3cd0f534e96e88c91373e8ed1c44 HEAD
1925 output = self.bare_git.ls_remote('-q', '--symref', '--exit-code', name, 'HEAD')
1926
1927 for line in output.splitlines():
1928 lhs, rhs = line.split('\t', 1)
1929 if rhs == 'HEAD' and lhs.startswith('ref:'):
1930 return lhs[4:].strip()
1931
1932 return None
1933
Zac Livingstone4332262017-06-16 08:56:09 -06001934 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001935 try:
1936 # if revision (sha or tag) is not present then following function
1937 # throws an error.
1938 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1939 return True
1940 except GitError:
1941 # There is no such persistent revision. We have to fetch it.
1942 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001943
Julien Campergue335f5ef2013-10-16 11:02:35 +02001944 def _FetchArchive(self, tarpath, cwd=None):
1945 cmd = ['archive', '-v', '-o', tarpath]
1946 cmd.append('--remote=%s' % self.remote.url)
1947 cmd.append('--prefix=%s/' % self.relpath)
1948 cmd.append(self.revisionExpr)
1949
1950 command = GitCommand(self, cmd, cwd=cwd,
1951 capture_stdout=True,
1952 capture_stderr=True)
1953
1954 if command.Wait() != 0:
1955 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1956
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001957 def _RemoteFetch(self, name=None,
1958 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001959 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001960 quiet=False,
Mike Frysinger521d01b2020-02-17 01:51:49 -05001961 verbose=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001962 alt_dir=None,
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05001963 tags=True,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001964 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001965 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04001966 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07001967 force_sync=False,
George Engelbrecht9bc283e2020-04-02 12:36:09 -06001968 clone_filter=None,
1969 retry_fetches=2,
1970 retry_sleep_initial_sec=4.0,
1971 retry_exp_factor=2.0):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001972 is_sha1 = False
1973 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001974 # The depth should not be used when fetching to a mirror because
1975 # it will result in a shallow repository that cannot be cloned or
1976 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001977 # The repo project should also never be synced with partial depth.
1978 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1979 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001980
Shawn Pearce69e04d82014-01-29 12:48:54 -08001981 if depth:
1982 current_branch_only = True
1983
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001984 if ID_RE.match(self.revisionExpr) is not None:
1985 is_sha1 = True
1986
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001987 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001988 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001989 # this is a tag and its sha1 value should never change
1990 tag_name = self.revisionExpr[len(R_TAGS):]
1991
1992 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001993 if self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05001994 if verbose:
Tim Schumacher1f1596b2019-04-15 11:17:08 +02001995 print('Skipped fetching project %s (already have persistent ref)'
1996 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001997 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001998 if is_sha1 and not depth:
1999 # When syncing a specific commit and --depth is not set:
2000 # * if upstream is explicitly specified and is not a sha1, fetch only
2001 # upstream as users expect only upstream to be fetch.
2002 # Note: The commit might not be in upstream in which case the sync
2003 # will fail.
2004 # * otherwise, fetch all branches to make sure we end up with the
2005 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002006 if self.upstream:
2007 current_branch_only = not ID_RE.match(self.upstream)
2008 else:
2009 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002010
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002011 if not name:
2012 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002013
2014 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002015 remote = self.GetRemote(name)
2016 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002017 ssh_proxy = True
2018
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002020 if alt_dir and 'objects' == os.path.basename(alt_dir):
2021 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002022 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2023 remote = self.GetRemote(name)
2024
David Pursehouse8a68ff92012-09-24 12:15:13 +09002025 all_refs = self.bare_ref.all
2026 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002027 tmp = set()
2028
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302029 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002030 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002031 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002032 all_refs[r] = ref_id
2033 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034 continue
2035
David Pursehouse8a68ff92012-09-24 12:15:13 +09002036 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002037 continue
2038
David Pursehouse8a68ff92012-09-24 12:15:13 +09002039 r = 'refs/_alt/%s' % ref_id
2040 all_refs[r] = ref_id
2041 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002042 tmp.add(r)
2043
heping3d7bbc92017-04-12 19:51:47 +08002044 tmp_packed_lines = []
2045 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002046
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302047 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002048 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002049 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002050 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002051 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002052
heping3d7bbc92017-04-12 19:51:47 +08002053 tmp_packed = ''.join(tmp_packed_lines)
2054 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002055 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002056 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002057 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002058
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002059 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002060
Xin Li745be2e2019-06-03 11:24:30 -07002061 if clone_filter:
2062 git_require((2, 19, 0), fail=True, msg='partial clones')
2063 cmd.append('--filter=%s' % clone_filter)
Mike Frysingerf81c72e2020-02-19 15:50:00 -05002064 self.EnableRepositoryExtension('partialclone', self.remote.name)
Xin Li745be2e2019-06-03 11:24:30 -07002065
Conley Owensf97e8382015-01-21 11:12:46 -08002066 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002067 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002068 else:
2069 # If this repo has shallow objects, then we don't know which refs have
2070 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2071 # do this with projects that don't have shallow objects, since it is less
2072 # efficient.
2073 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2074 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002075
Mike Frysinger4847e052020-02-22 00:07:35 -05002076 if not verbose:
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002077 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002078 if not quiet and sys.stdout.isatty():
2079 cmd.append('--progress')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002080 if not self.worktree:
2081 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002082 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002083
Mike Frysingere57f1142019-03-18 21:27:54 -04002084 if force_sync:
2085 cmd.append('--force')
2086
David Pursehouse74cfd272015-10-14 10:50:15 +09002087 if prune:
2088 cmd.append('--prune')
2089
Martin Kellye4e94d22017-03-21 16:05:12 -07002090 if submodules:
2091 cmd.append('--recurse-submodules=on-demand')
2092
Kuang-che Wu6856f982019-11-25 12:37:55 +08002093 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002094 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002095 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002096 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002097 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002098 spec.append('tag')
2099 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002100
Chirayu Desaif7b64e32020-02-04 17:50:57 +05302101 if self.manifest.IsMirror and not current_branch_only:
2102 branch = None
2103 else:
2104 branch = self.revisionExpr
Kuang-che Wu6856f982019-11-25 12:37:55 +08002105 if (not self.manifest.IsMirror and is_sha1 and depth
David Pursehouseabdf7502020-02-12 14:58:39 +09002106 and git_require((1, 8, 3))):
Kuang-che Wu6856f982019-11-25 12:37:55 +08002107 # Shallow checkout of a specific commit, fetch from that commit and not
2108 # the heads only as the commit might be deeper in the history.
2109 spec.append(branch)
2110 else:
2111 if is_sha1:
2112 branch = self.upstream
2113 if branch is not None and branch.strip():
2114 if not branch.startswith('refs/'):
2115 branch = R_HEADS + branch
2116 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
2117
2118 # If mirroring repo and we cannot deduce the tag or branch to fetch, fetch
2119 # whole repo.
2120 if self.manifest.IsMirror and not spec:
2121 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
2122
2123 # If using depth then we should not get all the tags since they may
2124 # be outside of the depth.
Mike Frysingerc58ec4d2020-02-17 14:36:08 -05002125 if not tags or depth:
Kuang-che Wu6856f982019-11-25 12:37:55 +08002126 cmd.append('--no-tags')
2127 else:
2128 cmd.append('--tags')
2129 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
2130
Conley Owens80b87fe2014-05-09 17:13:44 -07002131 cmd.extend(spec)
2132
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002133 # At least one retry minimum due to git remote prune.
2134 retry_fetches = max(retry_fetches, 2)
2135 retry_cur_sleep = retry_sleep_initial_sec
2136 ok = prune_tried = False
2137 for try_n in range(retry_fetches):
Mike Frysinger31990f02020-02-17 01:35:18 -05002138 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy,
Mike Frysinger4847e052020-02-22 00:07:35 -05002139 merge_output=True, capture_stdout=quiet)
John L. Villalovos126e2982015-01-29 21:58:12 -08002140 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002141 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002142 ok = True
2143 break
George Engelbrecht9bc283e2020-04-02 12:36:09 -06002144
2145 # Retry later due to HTTP 429 Too Many Requests.
2146 elif ('error:' in gitcmd.stderr and
2147 'HTTP 429' in gitcmd.stderr):
2148 if not quiet:
2149 print('429 received, sleeping: %s sec' % retry_cur_sleep,
2150 file=sys.stderr)
2151 time.sleep(retry_cur_sleep)
2152 retry_cur_sleep = min(retry_exp_factor * retry_cur_sleep,
2153 MAXIMUM_RETRY_SLEEP_SEC)
2154 retry_cur_sleep *= (1 - random.uniform(-RETRY_JITTER_PERCENT,
2155 RETRY_JITTER_PERCENT))
2156 continue
2157
2158 # If this is not last attempt, try 'git remote prune'.
2159 elif (try_n < retry_fetches - 1 and
2160 'error:' in gitcmd.stderr and
2161 'git remote prune' in gitcmd.stderr and
2162 not prune_tried):
2163 prune_tried = True
John L. Villalovos126e2982015-01-29 21:58:12 -08002164 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002165 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002166 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002167 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002168 break
2169 continue
Brian Harring14a66742012-09-28 20:21:57 -07002170 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002171 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2172 # in sha1 mode, we just tried sync'ing from the upstream field; it
2173 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002174 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002175 elif ret < 0:
2176 # Git died with a signal, exit immediately
2177 break
Mike Frysinger31990f02020-02-17 01:35:18 -05002178 if not verbose:
2179 print('%s:\n%s' % (self.name, gitcmd.stdout), file=sys.stderr)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002180 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002181
2182 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002183 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002184 if old_packed != '':
2185 _lwrite(packed_refs, old_packed)
2186 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002187 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002188 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002189
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002190 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002191 # We just synced the upstream given branch; verify we
2192 # got what we wanted, else trigger a second run of all
2193 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002194 if not self._CheckForImmutableRevision():
Mike Frysinger521d01b2020-02-17 01:51:49 -05002195 # Sync the current branch only with depth set to None.
2196 # We always pass depth=None down to avoid infinite recursion.
2197 return self._RemoteFetch(
2198 name=name, quiet=quiet, verbose=verbose,
2199 current_branch_only=current_branch_only and depth,
2200 initial=False, alt_dir=alt_dir,
2201 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002202
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002203 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002204
Mike Frysingere50b6a72020-02-19 01:45:48 -05002205 def _ApplyCloneBundle(self, initial=False, quiet=False, verbose=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002206 if initial and \
2207 (self.manifest.manifestProject.config.GetString('repo.depth') or
2208 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002209 return False
2210
2211 remote = self.GetRemote(self.remote.name)
2212 bundle_url = remote.url + '/clone.bundle'
2213 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002214 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2215 'persistent-http',
2216 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002217 return False
2218
2219 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2220 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2221
2222 exist_dst = os.path.exists(bundle_dst)
2223 exist_tmp = os.path.exists(bundle_tmp)
2224
2225 if not initial and not exist_dst and not exist_tmp:
2226 return False
2227
2228 if not exist_dst:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002229 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet,
2230 verbose)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002231 if not exist_dst:
2232 return False
2233
2234 cmd = ['fetch']
Mike Frysinger4847e052020-02-22 00:07:35 -05002235 if not verbose:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002236 cmd.append('--quiet')
Mike Frysinger4847e052020-02-22 00:07:35 -05002237 if not quiet and sys.stdout.isatty():
2238 cmd.append('--progress')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002239 if not self.worktree:
2240 cmd.append('--update-head-ok')
2241 cmd.append(bundle_dst)
2242 for f in remote.fetch:
2243 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002244 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002245
2246 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002247 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002248 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002249 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002250 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002251 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002252
Mike Frysingere50b6a72020-02-19 01:45:48 -05002253 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet, verbose):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002254 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002255 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002256
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002257 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002258 if quiet:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002259 cmd += ['--silent', '--show-error']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002260 if os.path.exists(tmpPath):
2261 size = os.stat(tmpPath).st_size
2262 if size >= 1024:
2263 cmd += ['--continue-at', '%d' % (size,)]
2264 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002265 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002266 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002267 if cookiefile:
Mike Frysingerdc1d0e02020-02-09 16:20:06 -05002268 cmd += ['--cookie', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002269 if proxy:
2270 cmd += ['--proxy', proxy]
2271 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2272 cmd += ['--proxy', os.environ['http_proxy']]
2273 if srcUrl.startswith('persistent-https'):
2274 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2275 elif srcUrl.startswith('persistent-http'):
2276 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002277 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002278
Dave Borowitz137d0132015-01-02 11:12:54 -08002279 if IsTrace():
2280 Trace('%s', ' '.join(cmd))
Mike Frysingere50b6a72020-02-19 01:45:48 -05002281 if verbose:
2282 print('%s: Downloading bundle: %s' % (self.name, srcUrl))
2283 stdout = None if verbose else subprocess.PIPE
2284 stderr = None if verbose else subprocess.STDOUT
Dave Borowitz137d0132015-01-02 11:12:54 -08002285 try:
Mike Frysingere50b6a72020-02-19 01:45:48 -05002286 proc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr)
Dave Borowitz137d0132015-01-02 11:12:54 -08002287 except OSError:
2288 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002289
Mike Frysingere50b6a72020-02-19 01:45:48 -05002290 (output, _) = proc.communicate()
2291 curlret = proc.returncode
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002292
Dave Borowitz137d0132015-01-02 11:12:54 -08002293 if curlret == 22:
2294 # From curl man page:
2295 # 22: HTTP page not retrieved. The requested url was not found or
2296 # returned another error with the HTTP error code being 400 or above.
2297 # This return code only appears if -f, --fail is used.
Mike Frysinger4847e052020-02-22 00:07:35 -05002298 if verbose:
George Engelbrechtb6871892020-04-02 13:53:01 -06002299 print('%s: Unable to retrieve clone.bundle; ignoring.' % self.name)
2300 if output:
George Engelbrecht2fe84e12020-04-15 11:28:00 -06002301 print('Curl output:\n%s' % output)
Dave Borowitz137d0132015-01-02 11:12:54 -08002302 return False
Mike Frysingere50b6a72020-02-19 01:45:48 -05002303 elif curlret and not verbose and output:
2304 print('%s' % output, file=sys.stderr)
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002305
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002306 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002307 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002308 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002309 return True
2310 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002311 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002312 return False
2313 else:
2314 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002315
Kris Giesingc8d882a2014-12-23 13:02:32 -08002316 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002317 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002318 with open(path, 'rb') as f:
2319 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002320 return True
2321 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002322 if not quiet:
2323 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002324 return False
2325 except OSError:
2326 return False
2327
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002328 def _Checkout(self, rev, quiet=False):
2329 cmd = ['checkout']
2330 if quiet:
2331 cmd.append('-q')
2332 cmd.append(rev)
2333 cmd.append('--')
2334 if GitCommand(self, cmd).Wait() != 0:
2335 if self._allrefs:
2336 raise GitError('%s checkout %s ' % (self.name, rev))
2337
Mike Frysinger915fda12020-03-22 12:15:20 -04002338 def _CherryPick(self, rev, ffonly=False, record_origin=False):
Pierre Tardye5a21222011-03-24 16:28:18 +01002339 cmd = ['cherry-pick']
Mike Frysingerea431762020-03-22 12:14:01 -04002340 if ffonly:
2341 cmd.append('--ff')
Mike Frysinger915fda12020-03-22 12:15:20 -04002342 if record_origin:
2343 cmd.append('-x')
Pierre Tardye5a21222011-03-24 16:28:18 +01002344 cmd.append(rev)
2345 cmd.append('--')
2346 if GitCommand(self, cmd).Wait() != 0:
2347 if self._allrefs:
2348 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2349
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302350 def _LsRemote(self, refs):
2351 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302352 p = GitCommand(self, cmd, capture_stdout=True)
2353 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002354 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302355 return None
2356
Anthony King7bdac712014-07-16 12:56:40 +01002357 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002358 cmd = ['revert']
2359 cmd.append('--no-edit')
2360 cmd.append(rev)
2361 cmd.append('--')
2362 if GitCommand(self, cmd).Wait() != 0:
2363 if self._allrefs:
2364 raise GitError('%s revert %s ' % (self.name, rev))
2365
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002366 def _ResetHard(self, rev, quiet=True):
2367 cmd = ['reset', '--hard']
2368 if quiet:
2369 cmd.append('-q')
2370 cmd.append(rev)
2371 if GitCommand(self, cmd).Wait() != 0:
2372 raise GitError('%s reset --hard %s ' % (self.name, rev))
2373
Martin Kellye4e94d22017-03-21 16:05:12 -07002374 def _SyncSubmodules(self, quiet=True):
2375 cmd = ['submodule', 'update', '--init', '--recursive']
2376 if quiet:
2377 cmd.append('-q')
2378 if GitCommand(self, cmd).Wait() != 0:
2379 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2380
Anthony King7bdac712014-07-16 12:56:40 +01002381 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002382 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002383 if onto is not None:
2384 cmd.extend(['--onto', onto])
2385 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002386 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002387 raise GitError('%s rebase %s ' % (self.name, upstream))
2388
Pierre Tardy3d125942012-05-04 12:18:12 +02002389 def _FastForward(self, head, ffonly=False):
Mike Frysinger2b1345b2020-02-17 01:07:11 -05002390 cmd = ['merge', '--no-stat', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002391 if ffonly:
2392 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002393 if GitCommand(self, cmd).Wait() != 0:
2394 raise GitError('%s merge %s ' % (self.name, head))
2395
David Pursehousee8ace262020-02-13 12:41:15 +09002396 def _InitGitDir(self, mirror_git=None, force_sync=False, quiet=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002397 init_git_dir = not os.path.exists(self.gitdir)
2398 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002399 try:
2400 # Initialize the bare repository, which contains all of the objects.
2401 if init_obj_dir:
2402 os.makedirs(self.objdir)
2403 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002404
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002405 if self.use_git_worktrees:
2406 # Set up the m/ space to point to the worktree-specific ref space.
2407 # We'll update the worktree-specific ref space on each checkout.
2408 if self.manifest.branch:
2409 self.bare_git.symbolic_ref(
2410 '-m', 'redirecting to worktree scope',
2411 R_M + self.manifest.branch,
2412 R_WORKTREE_M + self.manifest.branch)
2413
2414 # Enable per-worktree config file support if possible. This is more a
2415 # nice-to-have feature for users rather than a hard requirement.
Adrien Bioteau65f51ad2020-07-24 14:56:20 +02002416 if git_require((2, 20, 0)):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002417 self.EnableRepositoryExtension('worktreeConfig')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002418
Kevin Degib1a07b82015-07-27 13:33:43 -06002419 # If we have a separate directory to hold refs, initialize it as well.
2420 if self.objdir != self.gitdir:
2421 if init_git_dir:
2422 os.makedirs(self.gitdir)
2423
2424 if init_obj_dir or init_git_dir:
2425 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2426 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002427 try:
2428 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2429 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002430 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002431 print("Retrying clone after deleting %s" %
2432 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002433 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002434 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2435 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002436 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002437 platform_utils.rmtree(platform_utils.realpath(self.worktree))
David Pursehousee8ace262020-02-13 12:41:15 +09002438 return self._InitGitDir(mirror_git=mirror_git, force_sync=False,
2439 quiet=quiet)
David Pursehouse145e35b2020-02-12 15:40:47 +09002440 except Exception:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002441 raise e
2442 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002443
Kevin Degi384b3c52014-10-16 16:02:58 -06002444 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002445 mp = self.manifest.manifestProject
2446 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002447
Kevin Degib1a07b82015-07-27 13:33:43 -06002448 if ref_dir or mirror_git:
2449 if not mirror_git:
2450 mirror_git = os.path.join(ref_dir, self.name + '.git')
2451 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2452 self.relpath + '.git')
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002453 worktrees_git = os.path.join(ref_dir, '.repo', 'worktrees',
2454 self.name + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002455
Kevin Degib1a07b82015-07-27 13:33:43 -06002456 if os.path.exists(mirror_git):
2457 ref_dir = mirror_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002458 elif os.path.exists(repo_git):
2459 ref_dir = repo_git
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002460 elif os.path.exists(worktrees_git):
2461 ref_dir = worktrees_git
Kevin Degib1a07b82015-07-27 13:33:43 -06002462 else:
2463 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002464
Kevin Degib1a07b82015-07-27 13:33:43 -06002465 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002466 if not os.path.isabs(ref_dir):
2467 # The alternate directory is relative to the object database.
2468 ref_dir = os.path.relpath(ref_dir,
2469 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002470 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2471 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002472
David Pursehousee8ace262020-02-13 12:41:15 +09002473 self._UpdateHooks(quiet=quiet)
Kevin Degib1a07b82015-07-27 13:33:43 -06002474
2475 m = self.manifest.manifestProject.config
2476 for key in ['user.name', 'user.email']:
2477 if m.Has(key, include_defaults=False):
2478 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002479 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002480 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002481 if self.manifest.IsMirror:
2482 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002483 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002484 self.config.SetString('core.bare', None)
2485 except Exception:
2486 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002487 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002488 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002489 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002490 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002491
David Pursehousee8ace262020-02-13 12:41:15 +09002492 def _UpdateHooks(self, quiet=False):
Jimmie Westera0444582012-10-24 13:44:42 +02002493 if os.path.exists(self.gitdir):
David Pursehousee8ace262020-02-13 12:41:15 +09002494 self._InitHooks(quiet=quiet)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002495
David Pursehousee8ace262020-02-13 12:41:15 +09002496 def _InitHooks(self, quiet=False):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002497 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002498 if not os.path.exists(hooks):
2499 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002500 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002501 name = os.path.basename(stock_hook)
2502
Victor Boivie65e0f352011-04-18 11:23:29 +02002503 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002504 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002505 # Don't install a Gerrit Code Review hook if this
2506 # project does not appear to use it for reviews.
2507 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002508 # Since the manifest project is one of those, but also
2509 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002510 continue
2511
2512 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002513 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002514 continue
2515 if os.path.exists(dst):
Mike Frysinger7951e142020-02-21 00:04:15 -05002516 # If the files are the same, we'll leave it alone. We create symlinks
2517 # below by default but fallback to hardlinks if the OS blocks them.
2518 # So if we're here, it's probably because we made a hardlink below.
2519 if not filecmp.cmp(stock_hook, dst, shallow=False):
David Pursehousee8ace262020-02-13 12:41:15 +09002520 if not quiet:
2521 _warn("%s: Not replacing locally modified %s hook",
2522 self.relpath, name)
Mike Frysinger7951e142020-02-21 00:04:15 -05002523 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002524 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002525 platform_utils.symlink(
2526 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002527 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002528 if e.errno == errno.EPERM:
Mike Frysinger7951e142020-02-21 00:04:15 -05002529 try:
2530 os.link(stock_hook, dst)
2531 except OSError:
2532 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002533 else:
2534 raise
2535
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002536 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002537 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002538 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002539 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002540 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002541 remote.review = self.remote.review
2542 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002543
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002544 if self.worktree:
2545 remote.ResetFetch(mirror=False)
2546 else:
2547 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002548 remote.Save()
2549
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002550 def _InitMRef(self):
2551 if self.manifest.branch:
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002552 if self.use_git_worktrees:
2553 # We can't update this ref with git worktrees until it exists.
2554 # We'll wait until the initial checkout to set it.
2555 if not os.path.exists(self.worktree):
2556 return
2557
2558 base = R_WORKTREE_M
2559 active_git = self.work_git
2560 else:
2561 base = R_M
2562 active_git = self.bare_git
2563
2564 self._InitAnyMRef(base + self.manifest.branch, active_git)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002565
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002566 def _InitMirrorHead(self):
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002567 self._InitAnyMRef(HEAD, self.bare_git)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002568
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002569 def _InitAnyMRef(self, ref, active_git):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002570 cur = self.bare_ref.symref(ref)
2571
2572 if self.revisionId:
2573 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2574 msg = 'manifest set to %s' % self.revisionId
2575 dst = self.revisionId + '^0'
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002576 active_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002577 else:
2578 remote = self.GetRemote(self.remote.name)
2579 dst = remote.ToLocal(self.revisionExpr)
2580 if cur != dst:
2581 msg = 'manifest set to %s' % self.revisionExpr
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002582 active_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002583
Kevin Degi384b3c52014-10-16 16:02:58 -06002584 def _CheckDirReference(self, srcdir, destdir, share_refs):
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002585 # Git worktrees don't use symlinks to share at all.
2586 if self.use_git_worktrees:
2587 return
2588
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002589 symlink_files = self.shareable_files[:]
2590 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002591 if share_refs:
2592 symlink_files += self.working_tree_files
2593 symlink_dirs += self.working_tree_dirs
2594 to_symlink = symlink_files + symlink_dirs
2595 for name in set(to_symlink):
Mike Frysingered4f2112020-02-11 23:06:29 -05002596 # Try to self-heal a bit in simple cases.
2597 dst_path = os.path.join(destdir, name)
2598 src_path = os.path.join(srcdir, name)
2599
2600 if name in self.working_tree_dirs:
2601 # If the dir is missing under .repo/projects/, create it.
2602 if not os.path.exists(src_path):
2603 os.makedirs(src_path)
2604
2605 elif name in self.working_tree_files:
2606 # If it's a file under the checkout .git/ and the .repo/projects/ has
2607 # nothing, move the file under the .repo/projects/ tree.
2608 if not os.path.exists(src_path) and os.path.isfile(dst_path):
2609 platform_utils.rename(dst_path, src_path)
2610
2611 # If the path exists under the .repo/projects/ and there's no symlink
2612 # under the checkout .git/, recreate the symlink.
2613 if name in self.working_tree_dirs or name in self.working_tree_files:
2614 if os.path.exists(src_path) and not os.path.exists(dst_path):
2615 platform_utils.symlink(
2616 os.path.relpath(src_path, os.path.dirname(dst_path)), dst_path)
2617
2618 dst = platform_utils.realpath(dst_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002619 if os.path.lexists(dst):
Mike Frysingered4f2112020-02-11 23:06:29 -05002620 src = platform_utils.realpath(src_path)
Kevin Degi384b3c52014-10-16 16:02:58 -06002621 # Fail if the links are pointing to the wrong place
2622 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002623 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002624 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002625 'work tree. If you\'re comfortable with the '
2626 'possibility of losing the work tree\'s git metadata,'
2627 ' use `repo sync --force-sync {0}` to '
2628 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002629
David James8d201162013-10-11 17:03:19 -07002630 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2631 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2632
2633 Args:
2634 gitdir: The bare git repository. Must already be initialized.
2635 dotgit: The repository you would like to initialize.
2636 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2637 Only one work tree can store refs under a given |gitdir|.
2638 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2639 This saves you the effort of initializing |dotgit| yourself.
2640 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002641 symlink_files = self.shareable_files[:]
2642 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002643 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002644 symlink_files += self.working_tree_files
2645 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002646 to_symlink = symlink_files + symlink_dirs
2647
2648 to_copy = []
2649 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002650 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002651
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002652 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002653 for name in set(to_copy).union(to_symlink):
2654 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002655 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002656 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002657
Kevin Degi384b3c52014-10-16 16:02:58 -06002658 if os.path.lexists(dst):
2659 continue
David James8d201162013-10-11 17:03:19 -07002660
2661 # If the source dir doesn't exist, create an empty dir.
2662 if name in symlink_dirs and not os.path.lexists(src):
2663 os.makedirs(src)
2664
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002665 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002666 platform_utils.symlink(
2667 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002668 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002669 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002670 shutil.copytree(src, dst)
2671 elif os.path.isfile(src):
2672 shutil.copy(src, dst)
2673
Conley Owens80b87fe2014-05-09 17:13:44 -07002674 # If the source file doesn't exist, ensure the destination
2675 # file doesn't either.
2676 if name in symlink_files and not os.path.lexists(src):
2677 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002678 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002679 except OSError:
2680 pass
2681
David James8d201162013-10-11 17:03:19 -07002682 except OSError as e:
2683 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002684 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002685 else:
2686 raise
2687
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002688 def _InitGitWorktree(self):
2689 """Init the project using git worktrees."""
2690 self.bare_git.worktree('prune')
2691 self.bare_git.worktree('add', '-ff', '--checkout', '--detach', '--lock',
2692 self.worktree, self.GetRevisionId())
2693
2694 # Rewrite the internal state files to use relative paths between the
2695 # checkouts & worktrees.
2696 dotgit = os.path.join(self.worktree, '.git')
2697 with open(dotgit, 'r') as fp:
2698 # Figure out the checkout->worktree path.
2699 setting = fp.read()
2700 assert setting.startswith('gitdir:')
2701 git_worktree_path = setting.split(':', 1)[1].strip()
Mike Frysinger75264782020-02-21 18:55:07 -05002702 # Some platforms (e.g. Windows) won't let us update dotgit in situ because
2703 # of file permissions. Delete it and recreate it from scratch to avoid.
2704 platform_utils.remove(dotgit)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002705 # Use relative path from checkout->worktree & maintain Unix line endings
2706 # on all OS's to match git behavior.
2707 with open(dotgit, 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002708 print('gitdir:', os.path.relpath(git_worktree_path, self.worktree),
2709 file=fp)
Remy Bohmer44bc9642020-11-20 21:19:10 +01002710 # Use relative path from worktree->checkout & maintain Unix line endings
2711 # on all OS's to match git behavior.
2712 with open(os.path.join(git_worktree_path, 'gitdir'), 'w', newline='\n') as fp:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002713 print(os.path.relpath(dotgit, git_worktree_path), file=fp)
2714
Mike Frysinger21b7fbe2020-02-26 23:53:36 -05002715 self._InitMRef()
2716
Martin Kellye4e94d22017-03-21 16:05:12 -07002717 def _InitWorkTree(self, force_sync=False, submodules=False):
Mike Frysingerf4545122019-11-11 04:34:16 -05002718 realdotgit = os.path.join(self.worktree, '.git')
2719 tmpdotgit = realdotgit + '.tmp'
2720 init_dotgit = not os.path.exists(realdotgit)
2721 if init_dotgit:
Mike Frysinger979d5bd2020-02-09 02:28:34 -05002722 if self.use_git_worktrees:
2723 self._InitGitWorktree()
2724 self._CopyAndLinkFiles()
2725 return
2726
Mike Frysingerf4545122019-11-11 04:34:16 -05002727 dotgit = tmpdotgit
2728 platform_utils.rmtree(tmpdotgit, ignore_errors=True)
2729 os.makedirs(tmpdotgit)
2730 self._ReferenceGitDir(self.gitdir, tmpdotgit, share_refs=True,
2731 copy_all=False)
2732 else:
2733 dotgit = realdotgit
2734
Kevin Degib1a07b82015-07-27 13:33:43 -06002735 try:
Mike Frysingerf4545122019-11-11 04:34:16 -05002736 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2737 except GitError as e:
2738 if force_sync and not init_dotgit:
2739 try:
2740 platform_utils.rmtree(dotgit)
2741 return self._InitWorkTree(force_sync=False, submodules=submodules)
David Pursehouse145e35b2020-02-12 15:40:47 +09002742 except Exception:
Mike Frysingerf4545122019-11-11 04:34:16 -05002743 raise e
2744 raise e
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002745
Mike Frysingerf4545122019-11-11 04:34:16 -05002746 if init_dotgit:
2747 _lwrite(os.path.join(tmpdotgit, HEAD), '%s\n' % self.GetRevisionId())
Kevin Degi384b3c52014-10-16 16:02:58 -06002748
Mike Frysingerf4545122019-11-11 04:34:16 -05002749 # Now that the .git dir is fully set up, move it to its final home.
2750 platform_utils.rename(tmpdotgit, realdotgit)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002751
Mike Frysingerf4545122019-11-11 04:34:16 -05002752 # Finish checking out the worktree.
2753 cmd = ['read-tree', '--reset', '-u']
2754 cmd.append('-v')
2755 cmd.append(HEAD)
2756 if GitCommand(self, cmd).Wait() != 0:
2757 raise GitError('Cannot initialize work tree for ' + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002758
Mike Frysingerf4545122019-11-11 04:34:16 -05002759 if submodules:
2760 self._SyncSubmodules(quiet=True)
2761 self._CopyAndLinkFiles()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002762
Renaud Paquay788e9622017-01-27 11:41:12 -08002763 def _get_symlink_error_message(self):
2764 if platform_utils.isWindows():
2765 return ('Unable to create symbolic link. Please re-run the command as '
2766 'Administrator, or see '
2767 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2768 'for other options.')
2769 return 'filesystem must support symlinks'
2770
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002771 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002772 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002773
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002774 def _revlist(self, *args, **kw):
2775 a = []
2776 a.extend(args)
2777 a.append('--')
2778 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779
2780 @property
2781 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002782 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002783
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002784 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002785 """Get logs between two revisions of this project."""
2786 comp = '..'
2787 if rev1:
2788 revs = [rev1]
2789 if rev2:
2790 revs.extend([comp, rev2])
2791 cmd = ['log', ''.join(revs)]
2792 out = DiffColoring(self.config)
2793 if out.is_on and color:
2794 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002795 if pretty_format is not None:
2796 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002797 if oneline:
2798 cmd.append('--oneline')
2799
2800 try:
2801 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2802 if log.Wait() == 0:
2803 return log.stdout
2804 except GitError:
2805 # worktree may not exist if groups changed for example. In that case,
2806 # try in gitdir instead.
2807 if not os.path.exists(self.worktree):
2808 return self.bare_git.log(*cmd[1:])
2809 else:
2810 raise
2811 return None
2812
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002813 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2814 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002815 """Get the list of logs from this revision to given revisionId"""
2816 logs = {}
2817 selfId = self.GetRevisionId(self._allrefs)
2818 toId = toProject.GetRevisionId(toProject._allrefs)
2819
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002820 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2821 pretty_format=pretty_format)
2822 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2823 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002824 return logs
2825
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002826 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002827
David James8d201162013-10-11 17:03:19 -07002828 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002829 self._project = project
2830 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002831 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002832
Kimiyuki Onaka0501b292020-08-28 10:05:27 +09002833 # __getstate__ and __setstate__ are required for pickling because __getattr__ exists.
2834 def __getstate__(self):
2835 return (self._project, self._bare, self._gitdir)
2836
2837 def __setstate__(self, state):
2838 self._project, self._bare, self._gitdir = state
2839
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840 def LsOthers(self):
2841 p = GitCommand(self._project,
2842 ['ls-files',
2843 '-z',
2844 '--others',
2845 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002846 bare=False,
David James8d201162013-10-11 17:03:19 -07002847 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002848 capture_stdout=True,
2849 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002850 if p.Wait() == 0:
2851 out = p.stdout
2852 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002853 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002854 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002855 return []
2856
2857 def DiffZ(self, name, *args):
2858 cmd = [name]
2859 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002860 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002861 cmd.extend(args)
2862 p = GitCommand(self._project,
2863 cmd,
David James8d201162013-10-11 17:03:19 -07002864 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002865 bare=False,
2866 capture_stdout=True,
2867 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002868 try:
2869 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002870 if not hasattr(out, 'encode'):
2871 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002872 r = {}
2873 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002874 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002875 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002876 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002877 info = next(out)
2878 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002879 except StopIteration:
2880 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002881
2882 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002883
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002884 def __init__(self, path, omode, nmode, oid, nid, state):
2885 self.path = path
2886 self.src_path = None
2887 self.old_mode = omode
2888 self.new_mode = nmode
2889 self.old_id = oid
2890 self.new_id = nid
2891
2892 if len(state) == 1:
2893 self.status = state
2894 self.level = None
2895 else:
2896 self.status = state[:1]
2897 self.level = state[1:]
2898 while self.level.startswith('0'):
2899 self.level = self.level[1:]
2900
2901 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002902 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002903 if info.status in ('R', 'C'):
2904 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002905 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002906 r[info.path] = info
2907 return r
2908 finally:
2909 p.Wait()
2910
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002911 def GetDotgitPath(self, subpath=None):
2912 """Return the full path to the .git dir.
2913
2914 As a convenience, append |subpath| if provided.
2915 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002916 if self._bare:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002917 dotgit = self._gitdir
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002918 else:
Mike Frysinger4b0eb5a2020-02-23 23:22:34 -05002919 dotgit = os.path.join(self._project.worktree, '.git')
2920 if os.path.isfile(dotgit):
2921 # Git worktrees use a "gitdir:" syntax to point to the scratch space.
2922 with open(dotgit) as fp:
2923 setting = fp.read()
2924 assert setting.startswith('gitdir:')
2925 gitdir = setting.split(':', 1)[1].strip()
2926 dotgit = os.path.normpath(os.path.join(self._project.worktree, gitdir))
2927
2928 return dotgit if subpath is None else os.path.join(dotgit, subpath)
2929
2930 def GetHead(self):
2931 """Return the ref that HEAD points to."""
2932 path = self.GetDotgitPath(subpath=HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002933 try:
Mike Frysinger3164d402019-11-11 05:40:22 -05002934 with open(path) as fd:
2935 line = fd.readline()
Dan Sandler53e902a2014-03-09 13:20:02 -04002936 except IOError as e:
2937 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002938 try:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302939 line = line.decode()
2940 except AttributeError:
2941 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002942 if line.startswith('ref: '):
2943 return line[5:-1]
2944 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002945
2946 def SetHead(self, ref, message=None):
2947 cmdv = []
2948 if message is not None:
2949 cmdv.extend(['-m', message])
2950 cmdv.append(HEAD)
2951 cmdv.append(ref)
2952 self.symbolic_ref(*cmdv)
2953
2954 def DetachHead(self, new, message=None):
2955 cmdv = ['--no-deref']
2956 if message is not None:
2957 cmdv.extend(['-m', message])
2958 cmdv.append(HEAD)
2959 cmdv.append(new)
2960 self.update_ref(*cmdv)
2961
2962 def UpdateRef(self, name, new, old=None,
2963 message=None,
2964 detach=False):
2965 cmdv = []
2966 if message is not None:
2967 cmdv.extend(['-m', message])
2968 if detach:
2969 cmdv.append('--no-deref')
2970 cmdv.append(name)
2971 cmdv.append(new)
2972 if old is not None:
2973 cmdv.append(old)
2974 self.update_ref(*cmdv)
2975
2976 def DeleteRef(self, name, old=None):
2977 if not old:
2978 old = self.rev_parse(name)
2979 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002980 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002981
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002982 def rev_list(self, *args, **kw):
2983 if 'format' in kw:
2984 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2985 else:
2986 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002987 cmdv.extend(args)
2988 p = GitCommand(self._project,
2989 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002990 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002991 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002992 capture_stdout=True,
2993 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002994 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002995 raise GitError('%s rev-list %s: %s' %
2996 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002997 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002998
2999 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08003000 """Allow arbitrary git commands using pythonic syntax.
3001
3002 This allows you to do things like:
3003 git_obj.rev_parse('HEAD')
3004
3005 Since we don't have a 'rev_parse' method defined, the __getattr__ will
3006 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07003007 Any other positional arguments will be passed to the git command, and the
3008 following keyword arguments are supported:
3009 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08003010
3011 Args:
3012 name: The name of the git command to call. Any '_' characters will
3013 be replaced with '-'.
3014
3015 Returns:
3016 A callable object that will try to call git with the named command.
3017 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003018 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003019
Dave Borowitz091f8932012-10-23 17:01:04 -07003020 def runner(*args, **kwargs):
3021 cmdv = []
3022 config = kwargs.pop('config', None)
3023 for k in kwargs:
3024 raise TypeError('%s() got an unexpected keyword argument %r'
3025 % (name, k))
3026 if config is not None:
Chirayu Desai217ea7d2013-03-01 19:14:38 +05303027 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07003028 cmdv.append('-c')
3029 cmdv.append('%s=%s' % (k, v))
3030 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003031 cmdv.extend(args)
3032 p = GitCommand(self._project,
3033 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01003034 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07003035 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01003036 capture_stdout=True,
3037 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003038 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003039 raise GitError('%s %s: %s' %
3040 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003041 r = p.stdout
3042 if r.endswith('\n') and r.index('\n') == len(r) - 1:
3043 return r[:-1]
3044 return r
3045 return runner
3046
3047
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003048class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003049
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003050 def __str__(self):
3051 return 'prior sync failed; rebase still in progress'
3052
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003053
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003054class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003055
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003056 def __str__(self):
3057 return 'contains uncommitted changes'
3058
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003059
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003060class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003061
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003062 def __init__(self, project, text):
3063 self.project = project
3064 self.text = text
3065
3066 def Print(self, syncbuf):
3067 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3068 syncbuf.out.nl()
3069
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003070
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003071class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003072
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003073 def __init__(self, project, why):
3074 self.project = project
3075 self.why = why
3076
3077 def Print(self, syncbuf):
3078 syncbuf.out.fail('error: %s/: %s',
3079 self.project.relpath,
3080 str(self.why))
3081 syncbuf.out.nl()
3082
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003083
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003084class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003085
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003086 def __init__(self, project, action):
3087 self.project = project
3088 self.action = action
3089
3090 def Run(self, syncbuf):
3091 out = syncbuf.out
3092 out.project('project %s/', self.project.relpath)
3093 out.nl()
3094 try:
3095 self.action()
3096 out.nl()
3097 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003098 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003099 out.nl()
3100 return False
3101
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003102
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003103class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003104
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003105 def __init__(self, config):
3106 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003107 self.project = self.printer('header', attr='bold')
3108 self.info = self.printer('info')
3109 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003110
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003111
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003112class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003113
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003114 def __init__(self, config, detach_head=False):
3115 self._messages = []
3116 self._failures = []
3117 self._later_queue1 = []
3118 self._later_queue2 = []
3119
3120 self.out = _SyncColoring(config)
3121 self.out.redirect(sys.stderr)
3122
3123 self.detach_head = detach_head
3124 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003125 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003126
3127 def info(self, project, fmt, *args):
3128 self._messages.append(_InfoMessage(project, fmt % args))
3129
3130 def fail(self, project, err=None):
3131 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003132 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003133
3134 def later1(self, project, what):
3135 self._later_queue1.append(_Later(project, what))
3136
3137 def later2(self, project, what):
3138 self._later_queue2.append(_Later(project, what))
3139
3140 def Finish(self):
3141 self._PrintMessages()
3142 self._RunLater()
3143 self._PrintMessages()
3144 return self.clean
3145
David Rileye0684ad2017-04-05 00:02:59 -07003146 def Recently(self):
3147 recent_clean = self.recent_clean
3148 self.recent_clean = True
3149 return recent_clean
3150
3151 def _MarkUnclean(self):
3152 self.clean = False
3153 self.recent_clean = False
3154
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003155 def _RunLater(self):
3156 for q in ['_later_queue1', '_later_queue2']:
3157 if not self._RunQueue(q):
3158 return
3159
3160 def _RunQueue(self, queue):
3161 for m in getattr(self, queue):
3162 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003163 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003164 return False
3165 setattr(self, queue, [])
3166 return True
3167
3168 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003169 if self._messages or self._failures:
3170 if os.isatty(2):
3171 self.out.write(progress.CSI_ERASE_LINE)
3172 self.out.write('\r')
3173
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003174 for m in self._messages:
3175 m.Print(self)
3176 for m in self._failures:
3177 m.Print(self)
3178
3179 self._messages = []
3180 self._failures = []
3181
3182
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003183class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003184
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003185 """A special project housed under .repo.
3186 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003187
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003188 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003189 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003190 manifest=manifest,
3191 name=name,
3192 gitdir=gitdir,
3193 objdir=gitdir,
3194 worktree=worktree,
3195 remote=RemoteSpec('origin'),
3196 relpath='.repo/%s' % name,
3197 revisionExpr='refs/heads/master',
3198 revisionId=None,
3199 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003200
3201 def PreSync(self):
3202 if self.Exists:
3203 cb = self.CurrentBranch
3204 if cb:
3205 base = self.GetBranch(cb).merge
3206 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003207 self.revisionExpr = base
3208 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003209
Martin Kelly224a31a2017-07-10 14:46:25 -07003210 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003211 """ Prepare MetaProject for manifest branch switch
3212 """
3213
3214 # detach and delete manifest branch, allowing a new
3215 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003216 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003217 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003218 syncbuf.Finish()
3219
3220 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003221 ['update-ref', '-d', 'refs/heads/default'],
3222 capture_stdout=True,
3223 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003224
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003225 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003226 def LastFetch(self):
3227 try:
3228 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3229 return os.path.getmtime(fh)
3230 except OSError:
3231 return 0
3232
3233 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003234 def HasChanges(self):
3235 """Has the remote received new commits not yet checked out?
3236 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003237 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003238 return False
3239
David Pursehouse8a68ff92012-09-24 12:15:13 +09003240 all_refs = self.bare_ref.all
3241 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003242 head = self.work_git.GetHead()
3243 if head.startswith(R_HEADS):
3244 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003245 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003246 except KeyError:
3247 head = None
3248
3249 if revid == head:
3250 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003251 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003252 return True
3253 return False