blob: 03a75f49c06493333b62a9ed75fc19f4aaeec4d3 [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
Mike Frysingerf7c51602019-06-18 17:23:39 -040021import json
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070022import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070023import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070024import re
25import shutil
26import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070027import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070028import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020029import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080030import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070031import time
Dave Borowitz137d0132015-01-02 11:12:54 -080032import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070033
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070034from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070035from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070036from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
37 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070038from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080039from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080040from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070041import platform_utils
Mike Frysinger70d861f2019-08-26 15:22:36 -040042import progress
Mike Frysinger8a11f6f2019-08-27 00:26:15 -040043from repo_trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070044
Shawn O. Pearced237b692009-04-17 18:49:50 -070045from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070046
David Pursehouse59bbb582013-05-17 10:49:33 +090047from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040048if is_python3():
49 import urllib.parse
50else:
51 import imp
52 import urlparse
53 urllib = imp.new_module('urllib')
54 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053055 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053056
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070057
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070058def _lwrite(path, content):
59 lock = '%s.lock' % path
60
Chirayu Desai303a82f2014-08-19 22:57:17 +053061 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070062 try:
63 fd.write(content)
64 finally:
65 fd.close()
66
67 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070068 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080070 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070071 raise
72
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070073
Shawn O. Pearce48244782009-04-16 08:25:57 -070074def _error(fmt, *args):
75 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070076 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070077
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070078
David Pursehousef33929d2015-08-24 14:39:14 +090079def _warn(fmt, *args):
80 msg = fmt % args
81 print('warn: %s' % msg, file=sys.stderr)
82
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070084def not_rev(r):
85 return '^' + r
86
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070087
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080088def sq(r):
89 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080090
Jonathan Nieder93719792015-03-17 11:29:58 -070091_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070092
93
Jonathan Nieder93719792015-03-17 11:29:58 -070094def _ProjectHooks():
95 """List the hooks present in the 'hooks' directory.
96
97 These hooks are project hooks and are copied to the '.git/hooks' directory
98 of all subprojects.
99
100 This function caches the list of hooks (based on the contents of the
101 'repo/hooks' directory) on the first call.
102
103 Returns:
104 A list of absolute paths to all of the files in the hooks directory.
105 """
106 global _project_hook_list
107 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700108 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700109 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700110 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700111 return _project_hook_list
112
113
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700114class DownloadedChange(object):
115 _commit_cache = None
116
117 def __init__(self, project, base, change_id, ps_id, commit):
118 self.project = project
119 self.base = base
120 self.change_id = change_id
121 self.ps_id = ps_id
122 self.commit = commit
123
124 @property
125 def commits(self):
126 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700127 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
128 '--abbrev-commit',
129 '--pretty=oneline',
130 '--reverse',
131 '--date-order',
132 not_rev(self.base),
133 self.commit,
134 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700135 return self._commit_cache
136
137
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700138class ReviewableBranch(object):
139 _commit_cache = None
140
141 def __init__(self, project, branch, base):
142 self.project = project
143 self.branch = branch
144 self.base = base
145
146 @property
147 def name(self):
148 return self.branch.name
149
150 @property
151 def commits(self):
152 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700153 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
154 '--abbrev-commit',
155 '--pretty=oneline',
156 '--reverse',
157 '--date-order',
158 not_rev(self.base),
159 R_HEADS + self.name,
160 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700161 return self._commit_cache
162
163 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800164 def unabbrev_commits(self):
165 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700166 for commit in self.project.bare_git.rev_list(not_rev(self.base),
167 R_HEADS + self.name,
168 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800169 r[commit[0:8]] = commit
170 return r
171
172 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700173 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700174 return self.project.bare_git.log('--pretty=format:%cd',
175 '-n', '1',
176 R_HEADS + self.name,
177 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700178
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700179 def UploadForReview(self, people,
180 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000181 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200182 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700183 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200184 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200185 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800186 validate_certs=True,
187 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800188 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700189 people,
Brian Harring435370c2012-07-28 15:37:04 -0700190 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000191 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200192 private=private,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -0700193 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200194 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200195 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800196 validate_certs=validate_certs,
197 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700198
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700199 def GetPublishedRefs(self):
200 refs = {}
201 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700202 self.branch.remote.SshReviewUrl(self.project.UserEmail),
203 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700204 for line in output.split('\n'):
205 try:
206 (sha, ref) = line.split()
207 refs[sha] = ref
208 except ValueError:
209 pass
210
211 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700213
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700215
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216 def __init__(self, config):
217 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100218 self.project = self.printer('header', attr='bold')
219 self.branch = self.printer('header', attr='bold')
220 self.nobranch = self.printer('nobranch', fg='red')
221 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
Anthony King7bdac712014-07-16 12:56:40 +0100223 self.added = self.printer('added', fg='green')
224 self.changed = self.printer('changed', fg='red')
225 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226
227
228class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700229
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230 def __init__(self, config):
231 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100232 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700234
Anthony King7bdac712014-07-16 12:56:40 +0100235class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
James W. Mills24c13082012-04-12 15:04:13 -0500237 def __init__(self, name, value, keep):
238 self.name = name
239 self.value = value
240 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700241
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700242
Anthony King7bdac712014-07-16 12:56:40 +0100243class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700244
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800245 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 self.src = src
247 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800248 self.abs_src = abssrc
249 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250
251 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800252 src = self.abs_src
253 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700254 # copy file if it does not exist or is out of date
255 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
256 try:
257 # remove existing file first, since it might be read-only
258 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800259 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400260 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200261 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700262 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200263 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700264 shutil.copy(src, dest)
265 # make the file read-only
266 mode = os.stat(dest)[stat.ST_MODE]
267 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
268 os.chmod(dest, mode)
269 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700270 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700271
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700272
Anthony King7bdac712014-07-16 12:56:40 +0100273class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700274
Wink Saville4c426ef2015-06-03 08:05:17 -0700275 def __init__(self, git_worktree, src, dest, relsrc, absdest):
276 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 self.src = src
278 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700279 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500280 self.abs_dest = absdest
281
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700284 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 try:
286 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800287 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800288 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500289 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700290 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700291 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500292 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700293 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500294 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700295 _error('Cannot link file %s to %s', relSrc, absDest)
296
297 def _Link(self):
298 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
299 on the src linking all of the files in the source in to the destination
300 directory.
301 """
302 # We use the absSrc to handle the situation where the current directory
303 # is not the root of the repo
304 absSrc = os.path.join(self.git_worktree, self.src)
305 if os.path.exists(absSrc):
306 # Entity exists so just a simple one to one link operation
307 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
308 else:
309 # Entity doesn't exist assume there is a wild card
310 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700311 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700312 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700313 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700314 else:
315 absSrcFiles = glob.glob(absSrc)
316 for absSrcFile in absSrcFiles:
317 # Create a releative path from source dir to destination dir
318 absSrcDir = os.path.dirname(absSrcFile)
319 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
320
321 # Get the source file name
322 srcFile = os.path.basename(absSrcFile)
323
324 # Now form the final full paths to srcFile. They will be
325 # absolute for the desintaiton and relative for the srouce.
326 absDest = os.path.join(absDestDir, srcFile)
327 relSrc = os.path.join(relSrcDir, srcFile)
328 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500329
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700330
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700331class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700332
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700333 def __init__(self,
334 name,
Anthony King7bdac712014-07-16 12:56:40 +0100335 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700336 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100337 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700338 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700339 orig_name=None,
340 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700341 self.name = name
342 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700343 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700344 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100345 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700346 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700347 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700348
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Doug Anderson37282b42011-03-04 11:54:18 -0800350class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700351
Doug Anderson37282b42011-03-04 11:54:18 -0800352 """A RepoHook contains information about a script to run as a hook.
353
354 Hooks are used to run a python script before running an upload (for instance,
355 to run presubmit checks). Eventually, we may have hooks for other actions.
356
357 This shouldn't be confused with files in the 'repo/hooks' directory. Those
358 files are copied into each '.git/hooks' folder for each project. Repo-level
359 hooks are associated instead with repo actions.
360
361 Hooks are always python. When a hook is run, we will load the hook into the
362 interpreter and execute its main() function.
363 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700364
Doug Anderson37282b42011-03-04 11:54:18 -0800365 def __init__(self,
366 hook_type,
367 hooks_project,
368 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400369 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800370 abort_if_user_denies=False):
371 """RepoHook constructor.
372
373 Params:
374 hook_type: A string representing the type of hook. This is also used
375 to figure out the name of the file containing the hook. For
376 example: 'pre-upload'.
377 hooks_project: The project containing the repo hooks. If you have a
378 manifest, this is manifest.repo_hooks_project. OK if this is None,
379 which will make the hook a no-op.
380 topdir: Repo's top directory (the one containing the .repo directory).
381 Scripts will run with CWD as this directory. If you have a manifest,
382 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400383 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800384 abort_if_user_denies: If True, we'll throw a HookError() if the user
385 doesn't allow us to run the hook.
386 """
387 self._hook_type = hook_type
388 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400389 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800390 self._topdir = topdir
391 self._abort_if_user_denies = abort_if_user_denies
392
393 # Store the full path to the script for convenience.
394 if self._hooks_project:
395 self._script_fullpath = os.path.join(self._hooks_project.worktree,
396 self._hook_type + '.py')
397 else:
398 self._script_fullpath = None
399
400 def _GetHash(self):
401 """Return a hash of the contents of the hooks directory.
402
403 We'll just use git to do this. This hash has the property that if anything
404 changes in the directory we will return a different has.
405
406 SECURITY CONSIDERATION:
407 This hash only represents the contents of files in the hook directory, not
408 any other files imported or called by hooks. Changes to imported files
409 can change the script behavior without affecting the hash.
410
411 Returns:
412 A string representing the hash. This will always be ASCII so that it can
413 be printed to the user easily.
414 """
415 assert self._hooks_project, "Must have hooks to calculate their hash."
416
417 # We will use the work_git object rather than just calling GetRevisionId().
418 # That gives us a hash of the latest checked in version of the files that
419 # the user will actually be executing. Specifically, GetRevisionId()
420 # doesn't appear to change even if a user checks out a different version
421 # of the hooks repo (via git checkout) nor if a user commits their own revs.
422 #
423 # NOTE: Local (non-committed) changes will not be factored into this hash.
424 # I think this is OK, since we're really only worried about warning the user
425 # about upstream changes.
426 return self._hooks_project.work_git.rev_parse('HEAD')
427
428 def _GetMustVerb(self):
429 """Return 'must' if the hook is required; 'should' if not."""
430 if self._abort_if_user_denies:
431 return 'must'
432 else:
433 return 'should'
434
435 def _CheckForHookApproval(self):
436 """Check to see whether this hook has been approved.
437
Mike Frysinger40252c22016-08-15 21:23:44 -0400438 We'll accept approval of manifest URLs if they're using secure transports.
439 This way the user can say they trust the manifest hoster. For insecure
440 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800441
442 Note that we ask permission for each individual hook even though we use
443 the hash of all hooks when detecting changes. We'd like the user to be
444 able to approve / deny each hook individually. We only use the hash of all
445 hooks because there is no other easy way to detect changes to local imports.
446
447 Returns:
448 True if this hook is approved to run; False otherwise.
449
450 Raises:
451 HookError: Raised if the user doesn't approve and abort_if_user_denies
452 was passed to the consturctor.
453 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400454 if self._ManifestUrlHasSecureScheme():
455 return self._CheckForHookApprovalManifest()
456 else:
457 return self._CheckForHookApprovalHash()
458
459 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
460 changed_prompt):
461 """Check for approval for a particular attribute and hook.
462
463 Args:
464 subkey: The git config key under [repo.hooks.<hook_type>] to store the
465 last approved string.
466 new_val: The new value to compare against the last approved one.
467 main_prompt: Message to display to the user to ask for approval.
468 changed_prompt: Message explaining why we're re-asking for approval.
469
470 Returns:
471 True if this hook is approved to run; False otherwise.
Doug Anderson37282b42011-03-04 11:54:18 -0800472
Mike Frysinger40252c22016-08-15 21:23:44 -0400473 Raises:
474 HookError: Raised if the user doesn't approve and abort_if_user_denies
475 was passed to the consturctor.
476 """
477 hooks_config = self._hooks_project.config
478 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800479
Mike Frysinger40252c22016-08-15 21:23:44 -0400480 # Get the last value that the user approved for this hook; may be None.
481 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800482
Mike Frysinger40252c22016-08-15 21:23:44 -0400483 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800484 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400485 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800486 # Approval matched. We're done.
487 return True
488 else:
489 # Give the user a reason why we're prompting, since they last told
490 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400491 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800492 else:
493 prompt = ''
494
495 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
496 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400497 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530498 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900499 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800500
501 # User is doing a one-time approval.
502 if response in ('y', 'yes'):
503 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400504 elif response == 'always':
505 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800506 return True
507
508 # For anything else, we'll assume no approval.
509 if self._abort_if_user_denies:
510 raise HookError('You must allow the %s hook or use --no-verify.' %
511 self._hook_type)
512
513 return False
514
Mike Frysinger40252c22016-08-15 21:23:44 -0400515 def _ManifestUrlHasSecureScheme(self):
516 """Check if the URI for the manifest is a secure transport."""
517 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
518 parse_results = urllib.parse.urlparse(self._manifest_url)
519 return parse_results.scheme in secure_schemes
520
521 def _CheckForHookApprovalManifest(self):
522 """Check whether the user has approved this manifest host.
523
524 Returns:
525 True if this hook is approved to run; False otherwise.
526 """
527 return self._CheckForHookApprovalHelper(
528 'approvedmanifest',
529 self._manifest_url,
530 'Run hook scripts from %s' % (self._manifest_url,),
531 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
532
533 def _CheckForHookApprovalHash(self):
534 """Check whether the user has approved the hooks repo.
535
536 Returns:
537 True if this hook is approved to run; False otherwise.
538 """
539 prompt = ('Repo %s run the script:\n'
540 ' %s\n'
541 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700542 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400543 return self._CheckForHookApprovalHelper(
544 'approvedhash',
545 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700546 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400547 'Scripts have changed since %s was allowed.' % (self._hook_type,))
548
Mike Frysingerf7c51602019-06-18 17:23:39 -0400549 @staticmethod
550 def _ExtractInterpFromShebang(data):
551 """Extract the interpreter used in the shebang.
552
553 Try to locate the interpreter the script is using (ignoring `env`).
554
555 Args:
556 data: The file content of the script.
557
558 Returns:
559 The basename of the main script interpreter, or None if a shebang is not
560 used or could not be parsed out.
561 """
562 firstline = data.splitlines()[:1]
563 if not firstline:
564 return None
565
566 # The format here can be tricky.
567 shebang = firstline[0].strip()
568 m = re.match(r'^#!\s*([^\s]+)(?:\s+([^\s]+))?', shebang)
569 if not m:
570 return None
571
572 # If the using `env`, find the target program.
573 interp = m.group(1)
574 if os.path.basename(interp) == 'env':
575 interp = m.group(2)
576
577 return interp
578
579 def _ExecuteHookViaReexec(self, interp, context, **kwargs):
580 """Execute the hook script through |interp|.
581
582 Note: Support for this feature should be dropped ~Jun 2021.
583
584 Args:
585 interp: The Python program to run.
586 context: Basic Python context to execute the hook inside.
587 kwargs: Arbitrary arguments to pass to the hook script.
588
589 Raises:
590 HookError: When the hooks failed for any reason.
591 """
592 # This logic needs to be kept in sync with _ExecuteHookViaImport below.
593 script = """
594import json, os, sys
595path = '''%(path)s'''
596kwargs = json.loads('''%(kwargs)s''')
597context = json.loads('''%(context)s''')
598sys.path.insert(0, os.path.dirname(path))
599data = open(path).read()
600exec(compile(data, path, 'exec'), context)
601context['main'](**kwargs)
602""" % {
603 'path': self._script_fullpath,
604 'kwargs': json.dumps(kwargs),
605 'context': json.dumps(context),
606 }
607
608 # We pass the script via stdin to avoid OS argv limits. It also makes
609 # unhandled exception tracebacks less verbose/confusing for users.
610 cmd = [interp, '-c', 'import sys; exec(sys.stdin.read())']
611 proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
612 proc.communicate(input=script.encode('utf-8'))
613 if proc.returncode:
614 raise HookError('Failed to run %s hook.' % (self._hook_type,))
615
616 def _ExecuteHookViaImport(self, data, context, **kwargs):
617 """Execute the hook code in |data| directly.
618
619 Args:
620 data: The code of the hook to execute.
621 context: Basic Python context to execute the hook inside.
622 kwargs: Arbitrary arguments to pass to the hook script.
623
624 Raises:
625 HookError: When the hooks failed for any reason.
626 """
627 # Exec, storing global context in the context dict. We catch exceptions
628 # and convert to a HookError w/ just the failing traceback.
629 try:
630 exec(compile(data, self._script_fullpath, 'exec'), context)
631 except Exception:
632 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
633 (traceback.format_exc(), self._hook_type))
634
635 # Running the script should have defined a main() function.
636 if 'main' not in context:
637 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
638
639 # Call the main function in the hook. If the hook should cause the
640 # build to fail, it will raise an Exception. We'll catch that convert
641 # to a HookError w/ just the failing traceback.
642 try:
643 context['main'](**kwargs)
644 except Exception:
645 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
646 'above.' % (traceback.format_exc(), self._hook_type))
647
Doug Anderson37282b42011-03-04 11:54:18 -0800648 def _ExecuteHook(self, **kwargs):
649 """Actually execute the given hook.
650
651 This will run the hook's 'main' function in our python interpreter.
652
653 Args:
654 kwargs: Keyword arguments to pass to the hook. These are often specific
655 to the hook type. For instance, pre-upload hooks will contain
656 a project_list.
657 """
658 # Keep sys.path and CWD stashed away so that we can always restore them
659 # upon function exit.
660 orig_path = os.getcwd()
661 orig_syspath = sys.path
662
663 try:
664 # Always run hooks with CWD as topdir.
665 os.chdir(self._topdir)
666
667 # Put the hook dir as the first item of sys.path so hooks can do
668 # relative imports. We want to replace the repo dir as [0] so
669 # hooks can't import repo files.
670 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
671
Mike Frysingerf7c51602019-06-18 17:23:39 -0400672 # Initial global context for the hook to run within.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500673 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800674
Doug Anderson37282b42011-03-04 11:54:18 -0800675 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
676 # We don't actually want hooks to define their main with this argument--
677 # it's there to remind them that their hook should always take **kwargs.
678 # For instance, a pre-upload hook should be defined like:
679 # def main(project_list, **kwargs):
680 #
681 # This allows us to later expand the API without breaking old hooks.
682 kwargs = kwargs.copy()
683 kwargs['hook_should_take_kwargs'] = True
684
Mike Frysingerf7c51602019-06-18 17:23:39 -0400685 # See what version of python the hook has been written against.
686 data = open(self._script_fullpath).read()
687 interp = self._ExtractInterpFromShebang(data)
688 reexec = False
689 if interp:
690 prog = os.path.basename(interp)
691 if prog.startswith('python2') and sys.version_info.major != 2:
692 reexec = True
693 elif prog.startswith('python3') and sys.version_info.major == 2:
694 reexec = True
695
696 # Attempt to execute the hooks through the requested version of Python.
697 if reexec:
698 try:
699 self._ExecuteHookViaReexec(interp, context, **kwargs)
700 except OSError as e:
701 if e.errno == errno.ENOENT:
702 # We couldn't find the interpreter, so fallback to importing.
703 reexec = False
704 else:
705 raise
706
707 # Run the hook by importing directly.
708 if not reexec:
709 self._ExecuteHookViaImport(data, context, **kwargs)
Doug Anderson37282b42011-03-04 11:54:18 -0800710 finally:
711 # Restore sys.path and CWD.
712 sys.path = orig_syspath
713 os.chdir(orig_path)
714
715 def Run(self, user_allows_all_hooks, **kwargs):
716 """Run the hook.
717
718 If the hook doesn't exist (because there is no hooks project or because
719 this particular hook is not enabled), this is a no-op.
720
721 Args:
722 user_allows_all_hooks: If True, we will never prompt about running the
723 hook--we'll just assume it's OK to run it.
724 kwargs: Keyword arguments to pass to the hook. These are often specific
725 to the hook type. For instance, pre-upload hooks will contain
726 a project_list.
727
728 Raises:
729 HookError: If there was a problem finding the hook or the user declined
730 to run a required hook (from _CheckForHookApproval).
731 """
732 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700733 if ((not self._hooks_project) or (self._hook_type not in
734 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800735 return
736
737 # Bail with a nice error if we can't find the hook.
738 if not os.path.isfile(self._script_fullpath):
739 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
740
741 # Make sure the user is OK with running the hook.
742 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
743 return
744
745 # Run the hook with the same version of python we're using.
746 self._ExecuteHook(**kwargs)
747
748
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600750 # These objects can be shared between several working trees.
751 shareable_files = ['description', 'info']
752 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
753 # These objects can only be used by a single working tree.
754 working_tree_files = ['config', 'packed-refs', 'shallow']
755 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700756
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 def __init__(self,
758 manifest,
759 name,
760 remote,
761 gitdir,
David James8d201162013-10-11 17:03:19 -0700762 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700763 worktree,
764 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700765 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800766 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100767 rebase=True,
768 groups=None,
769 sync_c=False,
770 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900771 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100772 clone_depth=None,
773 upstream=None,
774 parent=None,
775 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900776 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700777 optimized_fetch=False,
778 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800779 """Init a Project object.
780
781 Args:
782 manifest: The XmlManifest object.
783 name: The `name` attribute of manifest.xml's project element.
784 remote: RemoteSpec object specifying its remote's properties.
785 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700786 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800787 worktree: Absolute path of git working tree.
788 relpath: Relative path of git working tree to repo's top directory.
789 revisionExpr: The `revision` attribute of manifest.xml's project element.
790 revisionId: git commit id for checking out.
791 rebase: The `rebase` attribute of manifest.xml's project element.
792 groups: The `groups` attribute of manifest.xml's project element.
793 sync_c: The `sync-c` attribute of manifest.xml's project element.
794 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900795 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800796 upstream: The `upstream` attribute of manifest.xml's project element.
797 parent: The parent Project object.
798 is_derived: False if the project was explicitly defined in the manifest;
799 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400800 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900801 optimized_fetch: If True, when a project is set to a sha1 revision, only
802 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700803 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800804 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805 self.manifest = manifest
806 self.name = name
807 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800808 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700809 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800810 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700811 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800812 else:
813 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700814 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700815 self.revisionExpr = revisionExpr
816
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700817 if revisionId is None \
818 and revisionExpr \
819 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700820 self.revisionId = revisionExpr
821 else:
822 self.revisionId = revisionId
823
Mike Pontillod3153822012-02-28 11:53:24 -0800824 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700825 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700826 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800827 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900828 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900829 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700830 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800831 self.parent = parent
832 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900833 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800834 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800835
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700836 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700837 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500838 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500839 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700840 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
841 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700842
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800843 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700844 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800845 else:
846 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700847 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700848 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700849 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400850 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700851 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700852
Doug Anderson37282b42011-03-04 11:54:18 -0800853 # This will be filled in if a project is later identified to be the
854 # project containing repo hooks.
855 self.enabled_repo_hooks = []
856
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700857 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800858 def Derived(self):
859 return self.is_derived
860
861 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700862 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700863 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700864
865 @property
866 def CurrentBranch(self):
867 """Obtain the name of the currently checked out branch.
868 The branch name omits the 'refs/heads/' prefix.
869 None is returned if the project is on a detached HEAD.
870 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700871 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872 if b.startswith(R_HEADS):
873 return b[len(R_HEADS):]
874 return None
875
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700876 def IsRebaseInProgress(self):
877 w = self.worktree
878 g = os.path.join(w, '.git')
879 return os.path.exists(os.path.join(g, 'rebase-apply')) \
880 or os.path.exists(os.path.join(g, 'rebase-merge')) \
881 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200882
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883 def IsDirty(self, consider_untracked=True):
884 """Is the working directory modified in some way?
885 """
886 self.work_git.update_index('-q',
887 '--unmerged',
888 '--ignore-missing',
889 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900890 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700891 return True
892 if self.work_git.DiffZ('diff-files'):
893 return True
894 if consider_untracked and self.work_git.LsOthers():
895 return True
896 return False
897
898 _userident_name = None
899 _userident_email = None
900
901 @property
902 def UserName(self):
903 """Obtain the user's personal name.
904 """
905 if self._userident_name is None:
906 self._LoadUserIdentity()
907 return self._userident_name
908
909 @property
910 def UserEmail(self):
911 """Obtain the user's email address. This is very likely
912 to be their Gerrit login.
913 """
914 if self._userident_email is None:
915 self._LoadUserIdentity()
916 return self._userident_email
917
918 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900919 u = self.bare_git.var('GIT_COMMITTER_IDENT')
920 m = re.compile("^(.*) <([^>]*)> ").match(u)
921 if m:
922 self._userident_name = m.group(1)
923 self._userident_email = m.group(2)
924 else:
925 self._userident_name = ''
926 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927
928 def GetRemote(self, name):
929 """Get the configuration for a single remote.
930 """
931 return self.config.GetRemote(name)
932
933 def GetBranch(self, name):
934 """Get the configuration for a single branch.
935 """
936 return self.config.GetBranch(name)
937
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700938 def GetBranches(self):
939 """Get all existing local branches.
940 """
941 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900942 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700943 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700944
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530945 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700946 if name.startswith(R_HEADS):
947 name = name[len(R_HEADS):]
948 b = self.GetBranch(name)
949 b.current = name == current
950 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900951 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700952 heads[name] = b
953
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530954 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700955 if name.startswith(R_PUB):
956 name = name[len(R_PUB):]
957 b = heads.get(name)
958 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900959 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700960
961 return heads
962
Colin Cross5acde752012-03-28 20:15:45 -0700963 def MatchesGroups(self, manifest_groups):
964 """Returns true if the manifest groups specified at init should cause
965 this project to be synced.
966 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700967 All projects are implicitly labelled with "all".
Colin Cross5acde752012-03-28 20:15:45 -0700968
Conley Owens971de8e2012-04-16 10:36:08 -0700969 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700970 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700971 manifest_groups: "-group1,group2"
972 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500973
974 The special manifest group "default" will match any project that
975 does not have the special project group "notdefault"
Conley Owens971de8e2012-04-16 10:36:08 -0700976 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500977 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700978 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700979 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500980 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700981
Conley Owens971de8e2012-04-16 10:36:08 -0700982 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700983 for group in expanded_manifest_groups:
984 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700985 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700986 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700987 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700988
Conley Owens971de8e2012-04-16 10:36:08 -0700989 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700990
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700991# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700992 def UncommitedFiles(self, get_all=True):
993 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700994
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700995 Args:
996 get_all: a boolean, if True - get information about all different
997 uncommitted files. If False - return as soon as any kind of
998 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500999 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001000 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001001 self.work_git.update_index('-q',
1002 '--unmerged',
1003 '--ignore-missing',
1004 '--refresh')
1005 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001006 details.append("rebase in progress")
1007 if not get_all:
1008 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001009
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001010 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
1011 if changes:
1012 details.extend(changes)
1013 if not get_all:
1014 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001015
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001016 changes = self.work_git.DiffZ('diff-files').keys()
1017 if changes:
1018 details.extend(changes)
1019 if not get_all:
1020 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001021
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001022 changes = self.work_git.LsOthers()
1023 if changes:
1024 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001025
Vadim Bendebury14e134d2014-10-05 15:40:30 -07001026 return details
1027
1028 def HasChanges(self):
1029 """Returns true if there are uncommitted changes.
1030 """
1031 if self.UncommitedFiles(get_all=False):
1032 return True
1033 else:
1034 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -05001035
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001036 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +02001038
1039 Args:
1040 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001041 quiet: If True then only print the project name. Do not print
1042 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 """
Renaud Paquaybed8b622018-09-27 10:46:58 -07001044 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001045 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +02001046 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -07001047 print(file=output_redir)
1048 print('project %s/' % self.relpath, file=output_redir)
1049 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050 return
1051
1052 self.work_git.update_index('-q',
1053 '--unmerged',
1054 '--ignore-missing',
1055 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001056 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
1058 df = self.work_git.DiffZ('diff-files')
1059 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +01001060 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001061 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001062
1063 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001064 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +02001065 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -07001066 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067
Andrew Wheeler4d5bb682012-02-27 13:52:22 -06001068 if quiet:
1069 out.nl()
1070 return 'DIRTY'
1071
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 branch = self.CurrentBranch
1073 if branch is None:
1074 out.nobranch('(*** NO BRANCH ***)')
1075 else:
1076 out.branch('branch %s', branch)
1077 out.nl()
1078
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001079 if rb:
1080 out.important('prior sync failed; rebase still in progress')
1081 out.nl()
1082
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083 paths = list()
1084 paths.extend(di.keys())
1085 paths.extend(df.keys())
1086 paths.extend(do)
1087
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301088 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001089 try:
1090 i = di[p]
1091 except KeyError:
1092 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001094 try:
1095 f = df[p]
1096 except KeyError:
1097 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +02001098
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001099 if i:
1100 i_status = i.status.upper()
1101 else:
1102 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103
David Pursehouse5c6eeac2012-10-11 16:44:48 +09001104 if f:
1105 f_status = f.status.lower()
1106 else:
1107 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001108
1109 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001110 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001111 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001112 else:
1113 line = ' %s%s\t%s' % (i_status, f_status, p)
1114
1115 if i and not f:
1116 out.added('%s', line)
1117 elif (i and f) or (not i and f):
1118 out.changed('%s', line)
1119 elif not i and not f:
1120 out.untracked('%s', line)
1121 else:
1122 out.write('%s', line)
1123 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001124
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001125 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001126
pelyad67872d2012-03-28 14:49:58 +03001127 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001128 """Prints the status of the repository to stdout.
1129 """
1130 out = DiffColoring(self.config)
1131 cmd = ['diff']
1132 if out.is_on:
1133 cmd.append('--color')
1134 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001135 if absolute_paths:
1136 cmd.append('--src-prefix=a/%s/' % self.relpath)
1137 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001138 cmd.append('--')
1139 p = GitCommand(self,
1140 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001141 capture_stdout=True,
1142 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001143 has_diff = False
1144 for line in p.process.stdout:
Mike Frysinger600f4922019-08-03 02:14:28 -04001145 if not hasattr(line, 'encode'):
1146 line = line.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001147 if not has_diff:
1148 out.nl()
1149 out.project('project %s/' % self.relpath)
1150 out.nl()
1151 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001152 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001153 p.Wait()
1154
1155
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001156# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001157
David Pursehouse8a68ff92012-09-24 12:15:13 +09001158 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001159 """Was the branch published (uploaded) for code review?
1160 If so, returns the SHA-1 hash of the last published
1161 state for the branch.
1162 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001163 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001164 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001165 try:
1166 return self.bare_git.rev_parse(key)
1167 except GitError:
1168 return None
1169 else:
1170 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001171 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001172 except KeyError:
1173 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001174
David Pursehouse8a68ff92012-09-24 12:15:13 +09001175 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001176 """Prunes any stale published refs.
1177 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001178 if all_refs is None:
1179 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001180 heads = set()
1181 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301182 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001183 if name.startswith(R_HEADS):
1184 heads.add(name)
1185 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001186 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301188 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001189 n = name[len(R_PUB):]
1190 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001191 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001193 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001194 """List any branches which can be uploaded for review.
1195 """
1196 heads = {}
1197 pubed = {}
1198
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301199 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001201 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001202 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001203 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001204
1205 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301206 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001207 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001208 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001209 if selected_branch and branch != selected_branch:
1210 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001211
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001212 rb = self.GetUploadableBranch(branch)
1213 if rb:
1214 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001215 return ready
1216
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001217 def GetUploadableBranch(self, branch_name):
1218 """Get a single uploadable branch, or None.
1219 """
1220 branch = self.GetBranch(branch_name)
1221 base = branch.LocalMerge
1222 if branch.LocalMerge:
1223 rb = ReviewableBranch(self, branch, base)
1224 if rb.commits:
1225 return rb
1226 return None
1227
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001228 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001229 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001230 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001231 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001232 private=False,
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001233 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001234 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001235 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001236 validate_certs=True,
1237 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001238 """Uploads the named branch for code review.
1239 """
1240 if branch is None:
1241 branch = self.CurrentBranch
1242 if branch is None:
1243 raise GitError('not currently on a branch')
1244
1245 branch = self.GetBranch(branch)
1246 if not branch.LocalMerge:
1247 raise GitError('branch %s does not track a remote' % branch.name)
1248 if not branch.remote.review:
1249 raise GitError('remote %s has no review url' % branch.remote.name)
1250
Bryan Jacobsf609f912013-05-06 13:36:24 -04001251 if dest_branch is None:
1252 dest_branch = self.dest_branch
1253 if dest_branch is None:
1254 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001255 if not dest_branch.startswith(R_HEADS):
1256 dest_branch = R_HEADS + dest_branch
1257
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001258 if not branch.remote.projectname:
1259 branch.remote.projectname = self.name
1260 branch.remote.Save()
1261
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001262 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001263 if url is None:
1264 raise UploadError('review not configured')
1265 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001266
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001267 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001268 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001269
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001270 for push_option in (push_options or []):
1271 cmd.append('-o')
1272 cmd.append(push_option)
1273
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001274 cmd.append(url)
1275
1276 if dest_branch.startswith(R_HEADS):
1277 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001278
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001279 upload_type = 'for'
1280 if draft:
1281 upload_type = 'drafts'
1282
1283 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1284 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001285 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001286 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001287 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001288
David Pursehousef25a3702018-11-14 19:01:22 -08001289 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001290 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendeburybd8f6582018-10-31 13:48:01 -07001291 if notify:
1292 opts += ['notify=' + notify]
Jonathan Nieder713c5872018-11-05 13:21:52 -08001293 if private:
1294 opts += ['private']
1295 if wip:
1296 opts += ['wip']
1297 if opts:
1298 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001299 cmd.append(ref_spec)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001300
Anthony King7bdac712014-07-16 12:56:40 +01001301 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001302 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001303
1304 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1305 self.bare_git.UpdateRef(R_PUB + branch.name,
1306 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001307 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001308
1309
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001310# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001311
Julien Campergue335f5ef2013-10-16 11:02:35 +02001312 def _ExtractArchive(self, tarpath, path=None):
1313 """Extract the given tar on its current location
1314
1315 Args:
1316 - tarpath: The path to the actual tar file
1317
1318 """
1319 try:
1320 with tarfile.open(tarpath, 'r') as tar:
1321 tar.extractall(path=path)
1322 return True
1323 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001324 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001325 return False
1326
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001327 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001328 quiet=False,
1329 is_new=None,
1330 current_branch_only=False,
1331 force_sync=False,
1332 clone_bundle=True,
1333 no_tags=False,
1334 archive=False,
1335 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001336 prune=False,
Xin Li745be2e2019-06-03 11:24:30 -07001337 submodules=False,
1338 clone_filter=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001339 """Perform only the network IO portion of the sync process.
1340 Local working directory/branch state is not affected.
1341 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001342 if archive and not isinstance(self, MetaProject):
1343 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001344 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001345 return False
1346
1347 name = self.relpath.replace('\\', '/')
1348 name = name.replace('/', '_')
1349 tarpath = '%s.tar' % name
1350 topdir = self.manifest.topdir
1351
1352 try:
1353 self._FetchArchive(tarpath, cwd=topdir)
1354 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001355 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001356 return False
1357
1358 # From now on, we only need absolute tarpath
1359 tarpath = os.path.join(topdir, tarpath)
1360
1361 if not self._ExtractArchive(tarpath, path=topdir):
1362 return False
1363 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001364 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001365 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001366 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001367 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001368 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001369 if is_new is None:
1370 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001371 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001372 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001373 else:
1374 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001375 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001376
1377 if is_new:
1378 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1379 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001380 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001381 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001382 # This works for both absolute and relative alternate directories.
1383 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001384 finally:
1385 fd.close()
1386 except IOError:
1387 alt_dir = None
1388 else:
1389 alt_dir = None
1390
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001391 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001392 and alt_dir is None \
1393 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001394 is_new = False
1395
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001396 if not current_branch_only:
1397 if self.sync_c:
1398 current_branch_only = True
1399 elif not self.manifest._loaded:
1400 # Manifest cannot check defaults until it syncs.
1401 current_branch_only = False
1402 elif self.manifest.default.sync_c:
1403 current_branch_only = True
1404
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001405 if not no_tags:
1406 if not self.sync_tags:
1407 no_tags = True
1408
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001409 if self.clone_depth:
1410 depth = self.clone_depth
1411 else:
1412 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1413
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001414 need_to_fetch = not (optimized_fetch and
1415 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001416 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001417 if (need_to_fetch and
1418 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1419 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001420 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001421 submodules=submodules, force_sync=force_sync,
1422 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001423 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001424
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001425 mp = self.manifest.manifestProject
1426 dissociate = mp.config.GetBoolean('repo.dissociate')
1427 if dissociate:
1428 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1429 if os.path.exists(alternates_file):
1430 cmd = ['repack', '-a', '-d']
1431 if GitCommand(self, cmd, bare=True).Wait() != 0:
1432 return False
1433 platform_utils.remove(alternates_file)
1434
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001435 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001436 self._InitMRef()
1437 else:
1438 self._InitMirrorHead()
1439 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001440 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001441 except OSError:
1442 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001444
1445 def PostRepoUpgrade(self):
1446 self._InitHooks()
1447
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001448 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001449 if self.manifest.isGitcClient:
1450 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001451 for copyfile in self.copyfiles:
1452 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001453 for linkfile in self.linkfiles:
1454 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455
Julien Camperguedd654222014-01-09 16:21:37 +01001456 def GetCommitRevisionId(self):
1457 """Get revisionId of a commit.
1458
1459 Use this method instead of GetRevisionId to get the id of the commit rather
1460 than the id of the current git object (for example, a tag)
1461
1462 """
1463 if not self.revisionExpr.startswith(R_TAGS):
1464 return self.GetRevisionId(self._allrefs)
1465
1466 try:
1467 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1468 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001469 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1470 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001471
David Pursehouse8a68ff92012-09-24 12:15:13 +09001472 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001473 if self.revisionId:
1474 return self.revisionId
1475
1476 rem = self.GetRemote(self.remote.name)
1477 rev = rem.ToLocal(self.revisionExpr)
1478
David Pursehouse8a68ff92012-09-24 12:15:13 +09001479 if all_refs is not None and rev in all_refs:
1480 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001481
1482 try:
1483 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1484 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001485 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1486 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001487
Martin Kellye4e94d22017-03-21 16:05:12 -07001488 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001489 """Perform only the local IO portion of the sync process.
1490 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001491 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001492 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001493 all_refs = self.bare_ref.all
1494 self.CleanPublishedCache(all_refs)
1495 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001496
David Pursehouse1d947b32012-10-25 12:23:11 +09001497 def _doff():
1498 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001499 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001500
Martin Kellye4e94d22017-03-21 16:05:12 -07001501 def _dosubmodules():
1502 self._SyncSubmodules(quiet=True)
1503
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001504 head = self.work_git.GetHead()
1505 if head.startswith(R_HEADS):
1506 branch = head[len(R_HEADS):]
1507 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001508 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001509 except KeyError:
1510 head = None
1511 else:
1512 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001513
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001514 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515 # Currently on a detached HEAD. The user is assumed to
1516 # not have any local modifications worth worrying about.
1517 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001518 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001519 syncbuf.fail(self, _PriorSyncFailedError())
1520 return
1521
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001522 if head == revid:
1523 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001524 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001525 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001526 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001527 # The copy/linkfile config may have changed.
1528 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001529 return
1530 else:
1531 lost = self._revlist(not_rev(revid), HEAD)
1532 if lost:
1533 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001534
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001535 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001536 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001537 if submodules:
1538 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001539 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001540 syncbuf.fail(self, e)
1541 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001542 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001543 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001545 if head == revid:
1546 # No changes; don't do anything further.
1547 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001548 # The copy/linkfile config may have changed.
1549 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001550 return
1551
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001552 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001554 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001556 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001558 syncbuf.info(self,
1559 "leaving %s; does not track upstream",
1560 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001561 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001562 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001563 if submodules:
1564 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001565 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001566 syncbuf.fail(self, e)
1567 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001568 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001569 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001570
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001571 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001572 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001573 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001574 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001575 if not_merged:
1576 if upstream_gain:
1577 # The user has published this branch and some of those
1578 # commits are not yet merged upstream. We do not want
1579 # to rewrite the published commits so we punt.
1580 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001581 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001582 "branch %s is published (but not merged) and is now "
1583 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001584 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001585 elif pub == head:
1586 # All published commits are merged, and thus we are a
1587 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001588 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001589 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001590 if submodules:
1591 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001592 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001593
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001594 # Examine the local commits not in the remote. Find the
1595 # last one attributed to this user, if any.
1596 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001597 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001598 last_mine = None
1599 cnt_mine = 0
1600 for commit in local_changes:
Mike Frysinger600f4922019-08-03 02:14:28 -04001601 commit_id, committer_email = commit.split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001602 if committer_email == self.UserEmail:
1603 last_mine = commit_id
1604 cnt_mine += 1
1605
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001606 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001607 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001608
1609 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001610 syncbuf.fail(self, _DirtyError())
1611 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001612
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001613 # If the upstream switched on us, warn the user.
1614 #
1615 if branch.merge != self.revisionExpr:
1616 if branch.merge and self.revisionExpr:
1617 syncbuf.info(self,
1618 'manifest switched %s...%s',
1619 branch.merge,
1620 self.revisionExpr)
1621 elif branch.merge:
1622 syncbuf.info(self,
1623 'manifest no longer tracks %s',
1624 branch.merge)
1625
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001626 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001627 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001628 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001629 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001630 syncbuf.info(self,
1631 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001632 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001633
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001634 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001635 if not ID_RE.match(self.revisionExpr):
1636 # in case of manifest sync the revisionExpr might be a SHA1
1637 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001638 if not branch.merge.startswith('refs/'):
1639 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001640 branch.Save()
1641
Mike Pontillod3153822012-02-28 11:53:24 -08001642 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001643 def _docopyandlink():
1644 self._CopyAndLinkFiles()
1645
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001646 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001647 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001648 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001649 if submodules:
1650 syncbuf.later2(self, _dosubmodules)
1651 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001652 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001653 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001654 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001655 if submodules:
1656 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001657 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001658 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001659 syncbuf.fail(self, e)
1660 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001661 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001662 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001663 if submodules:
1664 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001665
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001666 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001667 # dest should already be an absolute path, but src is project relative
1668 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001669 abssrc = os.path.join(self.worktree, src)
1670 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001671
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001672 def AddLinkFile(self, src, dest, absdest):
1673 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001674 # make src relative path to dest
1675 absdestdir = os.path.dirname(absdest)
1676 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001677 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001678
James W. Mills24c13082012-04-12 15:04:13 -05001679 def AddAnnotation(self, name, value, keep):
1680 self.annotations.append(_Annotation(name, value, keep))
1681
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001682 def DownloadPatchSet(self, change_id, patch_id):
1683 """Download a single patch set of a single change to FETCH_HEAD.
1684 """
1685 remote = self.GetRemote(self.remote.name)
1686
1687 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001688 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001689 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001690 if GitCommand(self, cmd, bare=True).Wait() != 0:
1691 return None
1692 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001693 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001694 change_id,
1695 patch_id,
1696 self.bare_git.rev_parse('FETCH_HEAD'))
1697
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001698
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001699# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001700
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001701 def StartBranch(self, name, branch_merge='', revision=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001702 """Create a new branch off the manifest's revision.
1703 """
Simran Basib9a1b732015-08-20 12:19:28 -07001704 if not branch_merge:
1705 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001706 head = self.work_git.GetHead()
1707 if head == (R_HEADS + name):
1708 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001709
David Pursehouse8a68ff92012-09-24 12:15:13 +09001710 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001711 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001712 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001713 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001714 capture_stdout=True,
1715 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001716
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001717 branch = self.GetBranch(name)
1718 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001719 branch.merge = branch_merge
1720 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1721 branch.merge = R_HEADS + branch_merge
Theodore Dubois60fdc5c2019-07-30 12:14:25 -07001722
1723 if revision is None:
1724 revid = self.GetRevisionId(all_refs)
1725 else:
1726 revid = self.work_git.rev_parse(revision)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001727
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001728 if head.startswith(R_HEADS):
1729 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001730 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001731 except KeyError:
1732 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001733 if revid and head and revid == head:
1734 ref = os.path.join(self.gitdir, R_HEADS + name)
1735 try:
1736 os.makedirs(os.path.dirname(ref))
1737 except OSError:
1738 pass
1739 _lwrite(ref, '%s\n' % revid)
1740 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1741 'ref: %s%s\n' % (R_HEADS, name))
1742 branch.Save()
1743 return True
1744
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001745 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001746 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001747 capture_stdout=True,
1748 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001749 branch.Save()
1750 return True
1751 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001752
Wink Saville02d79452009-04-10 13:01:24 -07001753 def CheckoutBranch(self, name):
1754 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001755
1756 Args:
1757 name: The name of the branch to checkout.
1758
1759 Returns:
1760 True if the checkout succeeded; False if it didn't; None if the branch
1761 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001762 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001763 rev = R_HEADS + name
1764 head = self.work_git.GetHead()
1765 if head == rev:
1766 # Already on the branch
1767 #
1768 return True
Wink Saville02d79452009-04-10 13:01:24 -07001769
David Pursehouse8a68ff92012-09-24 12:15:13 +09001770 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001771 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001772 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001773 except KeyError:
1774 # Branch does not exist in this project
1775 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001776 return None
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001777
1778 if head.startswith(R_HEADS):
1779 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001780 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001781 except KeyError:
1782 head = None
1783
1784 if head == revid:
1785 # Same revision; just update HEAD to point to the new
1786 # target branch, but otherwise take no other action.
1787 #
1788 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1789 'ref: %s%s\n' % (R_HEADS, name))
1790 return True
Wink Saville02d79452009-04-10 13:01:24 -07001791
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001792 return GitCommand(self,
1793 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001794 capture_stdout=True,
1795 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001796
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001797 def AbandonBranch(self, name):
1798 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001799
1800 Args:
1801 name: The name of the branch to abandon.
1802
1803 Returns:
1804 True if the abandon succeeded; False if it didn't; None if the branch
1805 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001806 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001807 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001808 all_refs = self.bare_ref.all
1809 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001810 # Doesn't exist
1811 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001812
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001813 head = self.work_git.GetHead()
1814 if head == rev:
1815 # We can't destroy the branch while we are sitting
1816 # on it. Switch to a detached HEAD.
1817 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001818 head = all_refs[head]
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001819
David Pursehouse8a68ff92012-09-24 12:15:13 +09001820 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001821 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001822 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1823 '%s\n' % revid)
1824 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001825 self._Checkout(revid, quiet=True)
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001826
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001827 return GitCommand(self,
1828 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001829 capture_stdout=True,
1830 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001831
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001832 def PruneHeads(self):
1833 """Prune any topic branches already merged into upstream.
1834 """
1835 cb = self.CurrentBranch
1836 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001837 left = self._allrefs
1838 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001839 if name.startswith(R_HEADS):
1840 name = name[len(R_HEADS):]
1841 if cb is None or name != cb:
1842 kill.append(name)
1843
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001844 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001845 if cb is not None \
1846 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001847 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001848 self.work_git.DetachHead(HEAD)
1849 kill.append(cb)
1850
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001851 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001852 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001853
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001854 try:
1855 self.bare_git.DetachHead(rev)
1856
1857 b = ['branch', '-d']
1858 b.extend(kill)
1859 b = GitCommand(self, b, bare=True,
1860 capture_stdout=True,
1861 capture_stderr=True)
1862 b.Wait()
1863 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001864 if ID_RE.match(old):
1865 self.bare_git.DetachHead(old)
1866 else:
1867 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001868 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001869
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001870 for branch in kill:
1871 if (R_HEADS + branch) not in left:
1872 self.CleanPublishedCache()
1873 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001874
1875 if cb and cb not in kill:
1876 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001877 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001878
1879 kept = []
1880 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001881 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001882 branch = self.GetBranch(branch)
1883 base = branch.LocalMerge
1884 if not base:
1885 base = rev
1886 kept.append(ReviewableBranch(self, branch, base))
1887 return kept
1888
1889
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001890# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001891
1892 def GetRegisteredSubprojects(self):
1893 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001894
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001895 def rec(subprojects):
1896 if not subprojects:
1897 return
1898 result.extend(subprojects)
1899 for p in subprojects:
1900 rec(p.subprojects)
1901 rec(self.subprojects)
1902 return result
1903
1904 def _GetSubmodules(self):
1905 # Unfortunately we cannot call `git submodule status --recursive` here
1906 # because the working tree might not exist yet, and it cannot be used
1907 # without a working tree in its current implementation.
1908
1909 def get_submodules(gitdir, rev):
1910 # Parse .gitmodules for submodule sub_paths and sub_urls
1911 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1912 if not sub_paths:
1913 return []
1914 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1915 # revision of submodule repository
1916 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1917 submodules = []
1918 for sub_path, sub_url in zip(sub_paths, sub_urls):
1919 try:
1920 sub_rev = sub_revs[sub_path]
1921 except KeyError:
1922 # Ignore non-exist submodules
1923 continue
1924 submodules.append((sub_rev, sub_path, sub_url))
1925 return submodules
1926
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001927 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1928 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001929
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001930 def parse_gitmodules(gitdir, rev):
1931 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1932 try:
Anthony King7bdac712014-07-16 12:56:40 +01001933 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1934 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001935 except GitError:
1936 return [], []
1937 if p.Wait() != 0:
1938 return [], []
1939
1940 gitmodules_lines = []
1941 fd, temp_gitmodules_path = tempfile.mkstemp()
1942 try:
1943 os.write(fd, p.stdout)
1944 os.close(fd)
1945 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001946 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1947 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001948 if p.Wait() != 0:
1949 return [], []
1950 gitmodules_lines = p.stdout.split('\n')
1951 except GitError:
1952 return [], []
1953 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001954 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001955
1956 names = set()
1957 paths = {}
1958 urls = {}
1959 for line in gitmodules_lines:
1960 if not line:
1961 continue
1962 m = re_path.match(line)
1963 if m:
1964 names.add(m.group(1))
1965 paths[m.group(1)] = m.group(2)
1966 continue
1967 m = re_url.match(line)
1968 if m:
1969 names.add(m.group(1))
1970 urls[m.group(1)] = m.group(2)
1971 continue
1972 names = sorted(names)
1973 return ([paths.get(name, '') for name in names],
1974 [urls.get(name, '') for name in names])
1975
1976 def git_ls_tree(gitdir, rev, paths):
1977 cmd = ['ls-tree', rev, '--']
1978 cmd.extend(paths)
1979 try:
Anthony King7bdac712014-07-16 12:56:40 +01001980 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1981 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001982 except GitError:
1983 return []
1984 if p.Wait() != 0:
1985 return []
1986 objects = {}
1987 for line in p.stdout.split('\n'):
1988 if not line.strip():
1989 continue
1990 object_rev, object_path = line.split()[2:4]
1991 objects[object_path] = object_rev
1992 return objects
1993
1994 try:
1995 rev = self.GetRevisionId()
1996 except GitError:
1997 return []
1998 return get_submodules(self.gitdir, rev)
1999
2000 def GetDerivedSubprojects(self):
2001 result = []
2002 if not self.Exists:
2003 # If git repo does not exist yet, querying its submodules will
2004 # mess up its states; so return here.
2005 return result
2006 for rev, path, url in self._GetSubmodules():
2007 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07002008 relpath, worktree, gitdir, objdir = \
2009 self.manifest.GetSubprojectPaths(self, name, path)
2010 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002011 if project:
2012 result.extend(project.GetDerivedSubprojects())
2013 continue
David James8d201162013-10-11 17:03:19 -07002014
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08002015 if url.startswith('..'):
2016 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002017 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01002018 url=url,
Steve Raed6480452016-08-10 15:00:00 -07002019 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01002020 review=self.remote.review,
2021 revision=self.remote.revision)
2022 subproject = Project(manifest=self.manifest,
2023 name=name,
2024 remote=remote,
2025 gitdir=gitdir,
2026 objdir=objdir,
2027 worktree=worktree,
2028 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02002029 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01002030 revisionId=rev,
2031 rebase=self.rebase,
2032 groups=self.groups,
2033 sync_c=self.sync_c,
2034 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09002035 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01002036 parent=self,
2037 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002038 result.append(subproject)
2039 result.extend(subproject.GetDerivedSubprojects())
2040 return result
2041
2042
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002043# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002044 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002045 try:
2046 # if revision (sha or tag) is not present then following function
2047 # throws an error.
2048 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2049 return True
2050 except GitError:
2051 # There is no such persistent revision. We have to fetch it.
2052 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002053
Julien Campergue335f5ef2013-10-16 11:02:35 +02002054 def _FetchArchive(self, tarpath, cwd=None):
2055 cmd = ['archive', '-v', '-o', tarpath]
2056 cmd.append('--remote=%s' % self.remote.url)
2057 cmd.append('--prefix=%s/' % self.relpath)
2058 cmd.append(self.revisionExpr)
2059
2060 command = GitCommand(self, cmd, cwd=cwd,
2061 capture_stdout=True,
2062 capture_stderr=True)
2063
2064 if command.Wait() != 0:
2065 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2066
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002067 def _RemoteFetch(self, name=None,
2068 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002069 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002070 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002071 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002072 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002073 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002074 depth=None,
Mike Frysingere57f1142019-03-18 21:27:54 -04002075 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07002076 force_sync=False,
2077 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002078
2079 is_sha1 = False
2080 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002081 # The depth should not be used when fetching to a mirror because
2082 # it will result in a shallow repository that cannot be cloned or
2083 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002084 # The repo project should also never be synced with partial depth.
2085 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2086 depth = None
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002087
Shawn Pearce69e04d82014-01-29 12:48:54 -08002088 if depth:
2089 current_branch_only = True
2090
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002091 if ID_RE.match(self.revisionExpr) is not None:
2092 is_sha1 = True
2093
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002094 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002095 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002096 # this is a tag and its sha1 value should never change
2097 tag_name = self.revisionExpr[len(R_TAGS):]
2098
2099 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002100 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002101 if not quiet:
2102 print('Skipped fetching project %s (already have persistent ref)'
2103 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002104 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002105 if is_sha1 and not depth:
2106 # When syncing a specific commit and --depth is not set:
2107 # * if upstream is explicitly specified and is not a sha1, fetch only
2108 # upstream as users expect only upstream to be fetch.
2109 # Note: The commit might not be in upstream in which case the sync
2110 # will fail.
2111 # * otherwise, fetch all branches to make sure we end up with the
2112 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002113 if self.upstream:
2114 current_branch_only = not ID_RE.match(self.upstream)
2115 else:
2116 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002117
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002118 if not name:
2119 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002120
2121 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002122 remote = self.GetRemote(name)
2123 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002124 ssh_proxy = True
2125
Shawn O. Pearce88443382010-10-08 10:02:09 +02002126 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002127 if alt_dir and 'objects' == os.path.basename(alt_dir):
2128 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002129 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2130 remote = self.GetRemote(name)
2131
David Pursehouse8a68ff92012-09-24 12:15:13 +09002132 all_refs = self.bare_ref.all
2133 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134 tmp = set()
2135
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302136 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002137 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002138 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002139 all_refs[r] = ref_id
2140 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002141 continue
2142
David Pursehouse8a68ff92012-09-24 12:15:13 +09002143 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002144 continue
2145
David Pursehouse8a68ff92012-09-24 12:15:13 +09002146 r = 'refs/_alt/%s' % ref_id
2147 all_refs[r] = ref_id
2148 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002149 tmp.add(r)
2150
heping3d7bbc92017-04-12 19:51:47 +08002151 tmp_packed_lines = []
2152 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002153
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302154 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002155 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002156 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002157 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002158 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002159
heping3d7bbc92017-04-12 19:51:47 +08002160 tmp_packed = ''.join(tmp_packed_lines)
2161 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002162 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002163 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002164 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002165
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002166 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002167
Xin Li745be2e2019-06-03 11:24:30 -07002168 if clone_filter:
2169 git_require((2, 19, 0), fail=True, msg='partial clones')
2170 cmd.append('--filter=%s' % clone_filter)
2171 self.config.SetString('extensions.partialclone', self.remote.name)
2172
Conley Owensf97e8382015-01-21 11:12:46 -08002173 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002174 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002175 else:
2176 # If this repo has shallow objects, then we don't know which refs have
2177 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2178 # do this with projects that don't have shallow objects, since it is less
2179 # efficient.
2180 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2181 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002182
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002183 if quiet:
2184 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002185 if not self.worktree:
2186 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002187 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002188
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002189 # If using depth then we should not get all the tags since they may
2190 # be outside of the depth.
2191 if no_tags or depth:
2192 cmd.append('--no-tags')
2193 else:
2194 cmd.append('--tags')
2195
Mike Frysingere57f1142019-03-18 21:27:54 -04002196 if force_sync:
2197 cmd.append('--force')
2198
David Pursehouse74cfd272015-10-14 10:50:15 +09002199 if prune:
2200 cmd.append('--prune')
2201
Martin Kellye4e94d22017-03-21 16:05:12 -07002202 if submodules:
2203 cmd.append('--recurse-submodules=on-demand')
2204
Conley Owens80b87fe2014-05-09 17:13:44 -07002205 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002206 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002207 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002208 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Xin Li3069be22019-08-22 10:00:27 -07002209 if not (no_tags or depth):
2210 spec.append(str((u'+refs/tags/*:') + remote.ToLocal('refs/tags/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002211 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002212 spec.append('tag')
2213 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002214
David Pursehouse403b64e2015-04-27 10:41:33 +09002215 if not self.manifest.IsMirror:
2216 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002217 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002218 # Shallow checkout of a specific commit, fetch from that commit and not
2219 # the heads only as the commit might be deeper in the history.
2220 spec.append(branch)
2221 else:
2222 if is_sha1:
2223 branch = self.upstream
2224 if branch is not None and branch.strip():
2225 if not branch.startswith('refs/'):
2226 branch = R_HEADS + branch
2227 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002228 cmd.extend(spec)
2229
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002230 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002231 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002232 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002233 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002234 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002235 ok = True
2236 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002237 # If needed, run the 'git remote prune' the first time through the loop
2238 elif (not _i and
2239 "error:" in gitcmd.stderr and
2240 "git remote prune" in gitcmd.stderr):
2241 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002242 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002243 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002244 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002245 break
2246 continue
Brian Harring14a66742012-09-28 20:21:57 -07002247 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002248 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2249 # in sha1 mode, we just tried sync'ing from the upstream field; it
2250 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002251 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002252 elif ret < 0:
2253 # Git died with a signal, exit immediately
2254 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002255 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002256
2257 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002258 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002259 if old_packed != '':
2260 _lwrite(packed_refs, old_packed)
2261 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002262 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002263 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002264
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002265 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002266 # We just synced the upstream given branch; verify we
2267 # got what we wanted, else trigger a second run of all
2268 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002269 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002270 if current_branch_only and depth:
2271 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002272 return self._RemoteFetch(name=name,
2273 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002274 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002275 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002276 else:
2277 # Avoid infinite recursion: sync all branches with depth set to None
2278 return self._RemoteFetch(name=name, current_branch_only=False,
2279 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002280 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002281
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002282 return ok
2283
2284 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002285 if initial and \
2286 (self.manifest.manifestProject.config.GetString('repo.depth') or
2287 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002288 return False
2289
2290 remote = self.GetRemote(self.remote.name)
2291 bundle_url = remote.url + '/clone.bundle'
2292 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002293 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2294 'persistent-http',
2295 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002296 return False
2297
2298 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2299 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002300
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002301 exist_dst = os.path.exists(bundle_dst)
2302 exist_tmp = os.path.exists(bundle_tmp)
2303
2304 if not initial and not exist_dst and not exist_tmp:
2305 return False
2306
2307 if not exist_dst:
2308 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2309 if not exist_dst:
2310 return False
2311
2312 cmd = ['fetch']
2313 if quiet:
2314 cmd.append('--quiet')
2315 if not self.worktree:
2316 cmd.append('--update-head-ok')
2317 cmd.append(bundle_dst)
2318 for f in remote.fetch:
2319 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002320 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002321
2322 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002323 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002324 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002325 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002326 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002327 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002328
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002329 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002330 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002331 platform_utils.remove(dstPath)
Shawn O. Pearce29472462011-10-11 09:24:07 -07002332
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002333 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002334 if quiet:
2335 cmd += ['--silent']
2336 if os.path.exists(tmpPath):
2337 size = os.stat(tmpPath).st_size
2338 if size >= 1024:
2339 cmd += ['--continue-at', '%d' % (size,)]
2340 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002341 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002342 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002343 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002344 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002345 if proxy:
2346 cmd += ['--proxy', proxy]
2347 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2348 cmd += ['--proxy', os.environ['http_proxy']]
2349 if srcUrl.startswith('persistent-https'):
2350 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2351 elif srcUrl.startswith('persistent-http'):
2352 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002353 cmd += [srcUrl]
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002354
Dave Borowitz137d0132015-01-02 11:12:54 -08002355 if IsTrace():
2356 Trace('%s', ' '.join(cmd))
2357 try:
2358 proc = subprocess.Popen(cmd)
2359 except OSError:
2360 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002361
Dave Borowitz137d0132015-01-02 11:12:54 -08002362 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002363
Dave Borowitz137d0132015-01-02 11:12:54 -08002364 if curlret == 22:
2365 # From curl man page:
2366 # 22: HTTP page not retrieved. The requested url was not found or
2367 # returned another error with the HTTP error code being 400 or above.
2368 # This return code only appears if -f, --fail is used.
2369 if not quiet:
2370 print("Server does not provide clone.bundle; ignoring.",
2371 file=sys.stderr)
2372 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002373
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002374 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002375 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002376 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002377 return True
2378 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002379 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002380 return False
2381 else:
2382 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002383
Kris Giesingc8d882a2014-12-23 13:02:32 -08002384 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002385 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002386 with open(path, 'rb') as f:
2387 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002388 return True
2389 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002390 if not quiet:
2391 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002392 return False
2393 except OSError:
2394 return False
2395
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002396 def _Checkout(self, rev, quiet=False):
2397 cmd = ['checkout']
2398 if quiet:
2399 cmd.append('-q')
2400 cmd.append(rev)
2401 cmd.append('--')
2402 if GitCommand(self, cmd).Wait() != 0:
2403 if self._allrefs:
2404 raise GitError('%s checkout %s ' % (self.name, rev))
2405
Anthony King7bdac712014-07-16 12:56:40 +01002406 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002407 cmd = ['cherry-pick']
2408 cmd.append(rev)
2409 cmd.append('--')
2410 if GitCommand(self, cmd).Wait() != 0:
2411 if self._allrefs:
2412 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2413
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302414 def _LsRemote(self, refs):
2415 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302416 p = GitCommand(self, cmd, capture_stdout=True)
2417 if p.Wait() == 0:
Mike Frysinger600f4922019-08-03 02:14:28 -04002418 return p.stdout
Akshay Vermacf7c0832018-03-15 21:56:30 +05302419 return None
2420
Anthony King7bdac712014-07-16 12:56:40 +01002421 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002422 cmd = ['revert']
2423 cmd.append('--no-edit')
2424 cmd.append(rev)
2425 cmd.append('--')
2426 if GitCommand(self, cmd).Wait() != 0:
2427 if self._allrefs:
2428 raise GitError('%s revert %s ' % (self.name, rev))
2429
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002430 def _ResetHard(self, rev, quiet=True):
2431 cmd = ['reset', '--hard']
2432 if quiet:
2433 cmd.append('-q')
2434 cmd.append(rev)
2435 if GitCommand(self, cmd).Wait() != 0:
2436 raise GitError('%s reset --hard %s ' % (self.name, rev))
2437
Martin Kellye4e94d22017-03-21 16:05:12 -07002438 def _SyncSubmodules(self, quiet=True):
2439 cmd = ['submodule', 'update', '--init', '--recursive']
2440 if quiet:
2441 cmd.append('-q')
2442 if GitCommand(self, cmd).Wait() != 0:
2443 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2444
Anthony King7bdac712014-07-16 12:56:40 +01002445 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002446 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002447 if onto is not None:
2448 cmd.extend(['--onto', onto])
2449 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002450 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002451 raise GitError('%s rebase %s ' % (self.name, upstream))
2452
Pierre Tardy3d125942012-05-04 12:18:12 +02002453 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002454 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002455 if ffonly:
2456 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002457 if GitCommand(self, cmd).Wait() != 0:
2458 raise GitError('%s merge %s ' % (self.name, head))
2459
Kevin Degiabaa7f32014-11-12 11:27:45 -07002460 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002461 init_git_dir = not os.path.exists(self.gitdir)
2462 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002463 try:
2464 # Initialize the bare repository, which contains all of the objects.
2465 if init_obj_dir:
2466 os.makedirs(self.objdir)
2467 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002468
Kevin Degib1a07b82015-07-27 13:33:43 -06002469 # If we have a separate directory to hold refs, initialize it as well.
2470 if self.objdir != self.gitdir:
2471 if init_git_dir:
2472 os.makedirs(self.gitdir)
Kevin Degi384b3c52014-10-16 16:02:58 -06002473
Kevin Degib1a07b82015-07-27 13:33:43 -06002474 if init_obj_dir or init_git_dir:
2475 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2476 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002477 try:
2478 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2479 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002480 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002481 print("Retrying clone after deleting %s" %
2482 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002483 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002484 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2485 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002486 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002487 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002488 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2489 except:
2490 raise e
2491 raise e
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002492
Kevin Degib1a07b82015-07-27 13:33:43 -06002493 if init_git_dir:
2494 mp = self.manifest.manifestProject
2495 ref_dir = mp.config.GetString('repo.reference') or ''
Shawn O. Pearce88443382010-10-08 10:02:09 +02002496
Kevin Degib1a07b82015-07-27 13:33:43 -06002497 if ref_dir or mirror_git:
2498 if not mirror_git:
2499 mirror_git = os.path.join(ref_dir, self.name + '.git')
2500 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2501 self.relpath + '.git')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002502
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 if os.path.exists(mirror_git):
2504 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002505
Kevin Degib1a07b82015-07-27 13:33:43 -06002506 elif os.path.exists(repo_git):
2507 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002508
Kevin Degib1a07b82015-07-27 13:33:43 -06002509 else:
2510 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002511
Kevin Degib1a07b82015-07-27 13:33:43 -06002512 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002513 if not os.path.isabs(ref_dir):
2514 # The alternate directory is relative to the object database.
2515 ref_dir = os.path.relpath(ref_dir,
2516 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002517 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2518 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002519
Kevin Degib1a07b82015-07-27 13:33:43 -06002520 self._UpdateHooks()
Jimmie Westera0444582012-10-24 13:44:42 +02002521
Kevin Degib1a07b82015-07-27 13:33:43 -06002522 m = self.manifest.manifestProject.config
2523 for key in ['user.name', 'user.email']:
2524 if m.Has(key, include_defaults=False):
2525 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002526 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002527 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002528 if self.manifest.IsMirror:
2529 self.config.SetString('core.bare', 'true')
2530 else:
2531 self.config.SetString('core.bare', None)
2532 except Exception:
2533 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002534 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002535 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002536 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002537 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002538
Jimmie Westera0444582012-10-24 13:44:42 +02002539 def _UpdateHooks(self):
2540 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002541 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002542
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002543 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002544 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002545 if not os.path.exists(hooks):
2546 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002547 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002548 name = os.path.basename(stock_hook)
2549
Victor Boivie65e0f352011-04-18 11:23:29 +02002550 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002551 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002552 # Don't install a Gerrit Code Review hook if this
2553 # project does not appear to use it for reviews.
2554 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002555 # Since the manifest project is one of those, but also
2556 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002557 continue
2558
2559 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002560 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002561 continue
2562 if os.path.exists(dst):
2563 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002564 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002565 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002566 _warn("%s: Not replacing locally modified %s hook",
2567 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002568 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002569 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002570 platform_utils.symlink(
2571 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002572 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002573 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002574 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002575 else:
2576 raise
2577
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002578 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002579 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002580 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002581 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002582 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002583 remote.review = self.remote.review
2584 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002585
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002586 if self.worktree:
2587 remote.ResetFetch(mirror=False)
2588 else:
2589 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002590 remote.Save()
2591
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002592 def _InitMRef(self):
2593 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002594 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002596 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002597 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002598
2599 def _InitAnyMRef(self, ref):
2600 cur = self.bare_ref.symref(ref)
2601
2602 if self.revisionId:
2603 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2604 msg = 'manifest set to %s' % self.revisionId
2605 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002606 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002607 else:
2608 remote = self.GetRemote(self.remote.name)
2609 dst = remote.ToLocal(self.revisionExpr)
2610 if cur != dst:
2611 msg = 'manifest set to %s' % self.revisionExpr
2612 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002613
Kevin Degi384b3c52014-10-16 16:02:58 -06002614 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002615 symlink_files = self.shareable_files[:]
2616 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002617 if share_refs:
2618 symlink_files += self.working_tree_files
2619 symlink_dirs += self.working_tree_dirs
2620 to_symlink = symlink_files + symlink_dirs
2621 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002622 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002623 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002624 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002625 # Fail if the links are pointing to the wrong place
2626 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002627 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002628 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002629 'work tree. If you\'re comfortable with the '
2630 'possibility of losing the work tree\'s git metadata,'
2631 ' use `repo sync --force-sync {0}` to '
2632 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002633
David James8d201162013-10-11 17:03:19 -07002634 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2635 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2636
2637 Args:
2638 gitdir: The bare git repository. Must already be initialized.
2639 dotgit: The repository you would like to initialize.
2640 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2641 Only one work tree can store refs under a given |gitdir|.
2642 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2643 This saves you the effort of initializing |dotgit| yourself.
2644 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002645 symlink_files = self.shareable_files[:]
2646 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002647 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002648 symlink_files += self.working_tree_files
2649 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002650 to_symlink = symlink_files + symlink_dirs
2651
2652 to_copy = []
2653 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002654 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002655
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002656 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002657 for name in set(to_copy).union(to_symlink):
2658 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002659 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002660 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002661
Kevin Degi384b3c52014-10-16 16:02:58 -06002662 if os.path.lexists(dst):
2663 continue
David James8d201162013-10-11 17:03:19 -07002664
2665 # If the source dir doesn't exist, create an empty dir.
2666 if name in symlink_dirs and not os.path.lexists(src):
2667 os.makedirs(src)
2668
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002669 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002670 platform_utils.symlink(
2671 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002672 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002673 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002674 shutil.copytree(src, dst)
2675 elif os.path.isfile(src):
2676 shutil.copy(src, dst)
2677
Conley Owens80b87fe2014-05-09 17:13:44 -07002678 # If the source file doesn't exist, ensure the destination
2679 # file doesn't either.
2680 if name in symlink_files and not os.path.lexists(src):
2681 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002682 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002683 except OSError:
2684 pass
2685
David James8d201162013-10-11 17:03:19 -07002686 except OSError as e:
2687 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002688 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002689 else:
2690 raise
2691
Martin Kellye4e94d22017-03-21 16:05:12 -07002692 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002693 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002694 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002695 try:
2696 if init_dotgit:
2697 os.makedirs(dotgit)
2698 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2699 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002700
Kevin Degiabaa7f32014-11-12 11:27:45 -07002701 try:
2702 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2703 except GitError as e:
2704 if force_sync:
2705 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002706 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002707 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002708 except:
2709 raise e
2710 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002711
Kevin Degib1a07b82015-07-27 13:33:43 -06002712 if init_dotgit:
2713 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002714
Kevin Degib1a07b82015-07-27 13:33:43 -06002715 cmd = ['read-tree', '--reset', '-u']
2716 cmd.append('-v')
2717 cmd.append(HEAD)
2718 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002719 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002720
Martin Kellye4e94d22017-03-21 16:05:12 -07002721 if submodules:
2722 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002723 self._CopyAndLinkFiles()
2724 except Exception:
2725 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002726 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002727 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002728
Renaud Paquay788e9622017-01-27 11:41:12 -08002729 def _get_symlink_error_message(self):
2730 if platform_utils.isWindows():
2731 return ('Unable to create symbolic link. Please re-run the command as '
2732 'Administrator, or see '
2733 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2734 'for other options.')
2735 return 'filesystem must support symlinks'
2736
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002737 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002738 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002739
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002740 def _revlist(self, *args, **kw):
2741 a = []
2742 a.extend(args)
2743 a.append('--')
2744 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002745
2746 @property
2747 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002748 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002749
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002750 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002751 """Get logs between two revisions of this project."""
2752 comp = '..'
2753 if rev1:
2754 revs = [rev1]
2755 if rev2:
2756 revs.extend([comp, rev2])
2757 cmd = ['log', ''.join(revs)]
2758 out = DiffColoring(self.config)
2759 if out.is_on and color:
2760 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002761 if pretty_format is not None:
2762 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002763 if oneline:
2764 cmd.append('--oneline')
2765
2766 try:
2767 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2768 if log.Wait() == 0:
2769 return log.stdout
2770 except GitError:
2771 # worktree may not exist if groups changed for example. In that case,
2772 # try in gitdir instead.
2773 if not os.path.exists(self.worktree):
2774 return self.bare_git.log(*cmd[1:])
2775 else:
2776 raise
2777 return None
2778
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002779 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2780 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002781 """Get the list of logs from this revision to given revisionId"""
2782 logs = {}
2783 selfId = self.GetRevisionId(self._allrefs)
2784 toId = toProject.GetRevisionId(toProject._allrefs)
2785
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002786 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2787 pretty_format=pretty_format)
2788 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2789 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002790 return logs
2791
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002792 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002793
David James8d201162013-10-11 17:03:19 -07002794 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002795 self._project = project
2796 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002797 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002798
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002799 def LsOthers(self):
2800 p = GitCommand(self._project,
2801 ['ls-files',
2802 '-z',
2803 '--others',
2804 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002805 bare=False,
David James8d201162013-10-11 17:03:19 -07002806 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002807 capture_stdout=True,
2808 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002809 if p.Wait() == 0:
2810 out = p.stdout
2811 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002812 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002813 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814 return []
2815
2816 def DiffZ(self, name, *args):
2817 cmd = [name]
2818 cmd.append('-z')
Eli Ribble2d095da2019-05-02 18:21:42 -07002819 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002820 cmd.extend(args)
2821 p = GitCommand(self._project,
2822 cmd,
David James8d201162013-10-11 17:03:19 -07002823 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002824 bare=False,
2825 capture_stdout=True,
2826 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002827 try:
2828 out = p.process.stdout.read()
Mike Frysinger600f4922019-08-03 02:14:28 -04002829 if not hasattr(out, 'encode'):
2830 out = out.decode()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002831 r = {}
2832 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002833 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002835 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002836 info = next(out)
2837 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002838 except StopIteration:
2839 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002840
2841 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002842
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002843 def __init__(self, path, omode, nmode, oid, nid, state):
2844 self.path = path
2845 self.src_path = None
2846 self.old_mode = omode
2847 self.new_mode = nmode
2848 self.old_id = oid
2849 self.new_id = nid
2850
2851 if len(state) == 1:
2852 self.status = state
2853 self.level = None
2854 else:
2855 self.status = state[:1]
2856 self.level = state[1:]
2857 while self.level.startswith('0'):
2858 self.level = self.level[1:]
2859
2860 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002861 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002862 if info.status in ('R', 'C'):
2863 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002864 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002865 r[info.path] = info
2866 return r
2867 finally:
2868 p.Wait()
2869
2870 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002871 if self._bare:
2872 path = os.path.join(self._project.gitdir, HEAD)
2873 else:
2874 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002875 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002876 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002877 except IOError as e:
2878 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002879 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002880 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002881 finally:
2882 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302883 try:
2884 line = line.decode()
2885 except AttributeError:
2886 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002887 if line.startswith('ref: '):
2888 return line[5:-1]
2889 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002890
2891 def SetHead(self, ref, message=None):
2892 cmdv = []
2893 if message is not None:
2894 cmdv.extend(['-m', message])
2895 cmdv.append(HEAD)
2896 cmdv.append(ref)
2897 self.symbolic_ref(*cmdv)
2898
2899 def DetachHead(self, new, message=None):
2900 cmdv = ['--no-deref']
2901 if message is not None:
2902 cmdv.extend(['-m', message])
2903 cmdv.append(HEAD)
2904 cmdv.append(new)
2905 self.update_ref(*cmdv)
2906
2907 def UpdateRef(self, name, new, old=None,
2908 message=None,
2909 detach=False):
2910 cmdv = []
2911 if message is not None:
2912 cmdv.extend(['-m', message])
2913 if detach:
2914 cmdv.append('--no-deref')
2915 cmdv.append(name)
2916 cmdv.append(new)
2917 if old is not None:
2918 cmdv.append(old)
2919 self.update_ref(*cmdv)
2920
2921 def DeleteRef(self, name, old=None):
2922 if not old:
2923 old = self.rev_parse(name)
2924 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002925 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002926
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002927 def rev_list(self, *args, **kw):
2928 if 'format' in kw:
2929 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2930 else:
2931 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002932 cmdv.extend(args)
2933 p = GitCommand(self._project,
2934 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002935 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002936 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002937 capture_stdout=True,
2938 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002939 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002940 raise GitError('%s rev-list %s: %s' %
2941 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002942 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943
2944 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002945 """Allow arbitrary git commands using pythonic syntax.
2946
2947 This allows you to do things like:
2948 git_obj.rev_parse('HEAD')
2949
2950 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2951 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002952 Any other positional arguments will be passed to the git command, and the
2953 following keyword arguments are supported:
2954 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002955
2956 Args:
2957 name: The name of the git command to call. Any '_' characters will
2958 be replaced with '-'.
2959
2960 Returns:
2961 A callable object that will try to call git with the named command.
2962 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002963 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002964
Dave Borowitz091f8932012-10-23 17:01:04 -07002965 def runner(*args, **kwargs):
2966 cmdv = []
2967 config = kwargs.pop('config', None)
2968 for k in kwargs:
2969 raise TypeError('%s() got an unexpected keyword argument %r'
2970 % (name, k))
2971 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002972 if not git_require((1, 7, 2)):
2973 raise ValueError('cannot set config on command line for %s()'
2974 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302975 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002976 cmdv.append('-c')
2977 cmdv.append('%s=%s' % (k, v))
2978 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002979 cmdv.extend(args)
2980 p = GitCommand(self._project,
2981 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002982 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002983 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002984 capture_stdout=True,
2985 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002986 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002987 raise GitError('%s %s: %s' %
2988 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002989 r = p.stdout
2990 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2991 return r[:-1]
2992 return r
2993 return runner
2994
2995
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002996class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002997
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002998 def __str__(self):
2999 return 'prior sync failed; rebase still in progress'
3000
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003001
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003002class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003003
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004 def __str__(self):
3005 return 'contains uncommitted changes'
3006
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003007
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003008class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003009
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003010 def __init__(self, project, text):
3011 self.project = project
3012 self.text = text
3013
3014 def Print(self, syncbuf):
3015 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
3016 syncbuf.out.nl()
3017
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003018
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003019class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003020
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003021 def __init__(self, project, why):
3022 self.project = project
3023 self.why = why
3024
3025 def Print(self, syncbuf):
3026 syncbuf.out.fail('error: %s/: %s',
3027 self.project.relpath,
3028 str(self.why))
3029 syncbuf.out.nl()
3030
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003031
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003032class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003033
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003034 def __init__(self, project, action):
3035 self.project = project
3036 self.action = action
3037
3038 def Run(self, syncbuf):
3039 out = syncbuf.out
3040 out.project('project %s/', self.project.relpath)
3041 out.nl()
3042 try:
3043 self.action()
3044 out.nl()
3045 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003046 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003047 out.nl()
3048 return False
3049
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003050
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003051class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003052
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003053 def __init__(self, config):
3054 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003055 self.project = self.printer('header', attr='bold')
3056 self.info = self.printer('info')
3057 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003058
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003059
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003060class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003061
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003062 def __init__(self, config, detach_head=False):
3063 self._messages = []
3064 self._failures = []
3065 self._later_queue1 = []
3066 self._later_queue2 = []
3067
3068 self.out = _SyncColoring(config)
3069 self.out.redirect(sys.stderr)
3070
3071 self.detach_head = detach_head
3072 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003073 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003074
3075 def info(self, project, fmt, *args):
3076 self._messages.append(_InfoMessage(project, fmt % args))
3077
3078 def fail(self, project, err=None):
3079 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003080 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003081
3082 def later1(self, project, what):
3083 self._later_queue1.append(_Later(project, what))
3084
3085 def later2(self, project, what):
3086 self._later_queue2.append(_Later(project, what))
3087
3088 def Finish(self):
3089 self._PrintMessages()
3090 self._RunLater()
3091 self._PrintMessages()
3092 return self.clean
3093
David Rileye0684ad2017-04-05 00:02:59 -07003094 def Recently(self):
3095 recent_clean = self.recent_clean
3096 self.recent_clean = True
3097 return recent_clean
3098
3099 def _MarkUnclean(self):
3100 self.clean = False
3101 self.recent_clean = False
3102
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003103 def _RunLater(self):
3104 for q in ['_later_queue1', '_later_queue2']:
3105 if not self._RunQueue(q):
3106 return
3107
3108 def _RunQueue(self, queue):
3109 for m in getattr(self, queue):
3110 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003111 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003112 return False
3113 setattr(self, queue, [])
3114 return True
3115
3116 def _PrintMessages(self):
Mike Frysinger70d861f2019-08-26 15:22:36 -04003117 if self._messages or self._failures:
3118 if os.isatty(2):
3119 self.out.write(progress.CSI_ERASE_LINE)
3120 self.out.write('\r')
3121
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003122 for m in self._messages:
3123 m.Print(self)
3124 for m in self._failures:
3125 m.Print(self)
3126
3127 self._messages = []
3128 self._failures = []
3129
3130
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003131class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003132
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003133 """A special project housed under .repo.
3134 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003136 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003137 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003138 manifest=manifest,
3139 name=name,
3140 gitdir=gitdir,
3141 objdir=gitdir,
3142 worktree=worktree,
3143 remote=RemoteSpec('origin'),
3144 relpath='.repo/%s' % name,
3145 revisionExpr='refs/heads/master',
3146 revisionId=None,
3147 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003148
3149 def PreSync(self):
3150 if self.Exists:
3151 cb = self.CurrentBranch
3152 if cb:
3153 base = self.GetBranch(cb).merge
3154 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003155 self.revisionExpr = base
3156 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003157
Martin Kelly224a31a2017-07-10 14:46:25 -07003158 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003159 """ Prepare MetaProject for manifest branch switch
3160 """
3161
3162 # detach and delete manifest branch, allowing a new
3163 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003164 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003165 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003166 syncbuf.Finish()
3167
3168 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003169 ['update-ref', '-d', 'refs/heads/default'],
3170 capture_stdout=True,
3171 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003172
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003173 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003174 def LastFetch(self):
3175 try:
3176 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3177 return os.path.getmtime(fh)
3178 except OSError:
3179 return 0
3180
3181 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003182 def HasChanges(self):
3183 """Has the remote received new commits not yet checked out?
3184 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003185 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003186 return False
3187
David Pursehouse8a68ff92012-09-24 12:15:13 +09003188 all_refs = self.bare_ref.all
3189 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003190 head = self.work_git.GetHead()
3191 if head.startswith(R_HEADS):
3192 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003193 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003194 except KeyError:
3195 head = None
3196
3197 if revid == head:
3198 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003199 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003200 return True
3201 return False