blob: b7ff2125b2c84d1c031863f002f9a43896d14738 [file] [log] [blame]
Mike Frysingerf6013762019-06-13 02:30:51 -04001# -*- coding:utf-8 -*-
2#
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
Sarah Owenscecd1d82012-11-01 22:59:27 -070017from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080018import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070020import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070022import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070023import re
24import shutil
25import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070026import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070027import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020028import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080029import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070030import time
Dave Borowitz137d0132015-01-02 11:12:54 -080031import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070032
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070033from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070034from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070035from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070036 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070037from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070038from error import CacheApplyError
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
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070042from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070043
Shawn O. Pearced237b692009-04-17 18:49:50 -070044from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070045
David Pursehouse59bbb582013-05-17 10:49:33 +090046from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040047if is_python3():
48 import urllib.parse
49else:
50 import imp
51 import urlparse
52 urllib = imp.new_module('urllib')
53 urllib.parse = urlparse
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053055
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070056
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070057def _lwrite(path, content):
58 lock = '%s.lock' % path
59
Chirayu Desai303a82f2014-08-19 22:57:17 +053060 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070061 try:
62 fd.write(content)
63 finally:
64 fd.close()
65
66 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070067 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070068 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080069 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070070 raise
71
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070072
Shawn O. Pearce48244782009-04-16 08:25:57 -070073def _error(fmt, *args):
74 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070075 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070076
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070077
David Pursehousef33929d2015-08-24 14:39:14 +090078def _warn(fmt, *args):
79 msg = fmt % args
80 print('warn: %s' % msg, file=sys.stderr)
81
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070082
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070083def not_rev(r):
84 return '^' + r
85
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070086
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080087def sq(r):
88 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080089
Jonathan Nieder93719792015-03-17 11:29:58 -070090_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070091
92
Jonathan Nieder93719792015-03-17 11:29:58 -070093def _ProjectHooks():
94 """List the hooks present in the 'hooks' directory.
95
96 These hooks are project hooks and are copied to the '.git/hooks' directory
97 of all subprojects.
98
99 This function caches the list of hooks (based on the contents of the
100 'repo/hooks' directory) on the first call.
101
102 Returns:
103 A list of absolute paths to all of the files in the hooks directory.
104 """
105 global _project_hook_list
106 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700107 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700108 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700109 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700110 return _project_hook_list
111
112
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700113class DownloadedChange(object):
114 _commit_cache = None
115
116 def __init__(self, project, base, change_id, ps_id, commit):
117 self.project = project
118 self.base = base
119 self.change_id = change_id
120 self.ps_id = ps_id
121 self.commit = commit
122
123 @property
124 def commits(self):
125 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700126 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
127 '--abbrev-commit',
128 '--pretty=oneline',
129 '--reverse',
130 '--date-order',
131 not_rev(self.base),
132 self.commit,
133 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700134 return self._commit_cache
135
136
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700137class ReviewableBranch(object):
138 _commit_cache = None
139
140 def __init__(self, project, branch, base):
141 self.project = project
142 self.branch = branch
143 self.base = base
144
145 @property
146 def name(self):
147 return self.branch.name
148
149 @property
150 def commits(self):
151 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700152 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
153 '--abbrev-commit',
154 '--pretty=oneline',
155 '--reverse',
156 '--date-order',
157 not_rev(self.base),
158 R_HEADS + self.name,
159 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700160 return self._commit_cache
161
162 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800163 def unabbrev_commits(self):
164 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700165 for commit in self.project.bare_git.rev_list(not_rev(self.base),
166 R_HEADS + self.name,
167 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800168 r[commit[0:8]] = commit
169 return r
170
171 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700172 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700173 return self.project.bare_git.log('--pretty=format:%cd',
174 '-n', '1',
175 R_HEADS + self.name,
176 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700177
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700178 def UploadForReview(self, people,
179 auto_topic=False,
180 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200181 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700182 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200183 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200184 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800185 validate_certs=True,
186 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800187 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700188 people,
Brian Harring435370c2012-07-28 15:37:04 -0700189 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400190 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200191 private=private,
Vadim Bendebury75bcd242018-10-31 13:48:01 -0700192 notify=notify,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200193 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200194 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800195 validate_certs=validate_certs,
196 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700197
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700198 def GetPublishedRefs(self):
199 refs = {}
200 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700201 self.branch.remote.SshReviewUrl(self.project.UserEmail),
202 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700203 for line in output.split('\n'):
204 try:
205 (sha, ref) = line.split()
206 refs[sha] = ref
207 except ValueError:
208 pass
209
210 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700212
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700214
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215 def __init__(self, config):
216 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100217 self.project = self.printer('header', attr='bold')
218 self.branch = self.printer('header', attr='bold')
219 self.nobranch = self.printer('nobranch', fg='red')
220 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700221
Anthony King7bdac712014-07-16 12:56:40 +0100222 self.added = self.printer('added', fg='green')
223 self.changed = self.printer('changed', fg='red')
224 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700225
226
227class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229 def __init__(self, config):
230 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100231 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700232
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
Anthony King7bdac712014-07-16 12:56:40 +0100234class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700235
James W. Mills24c13082012-04-12 15:04:13 -0500236 def __init__(self, name, value, keep):
237 self.name = name
238 self.value = value
239 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700241
Anthony King7bdac712014-07-16 12:56:40 +0100242class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700243
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700245 self.src = src
246 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800247 self.abs_src = abssrc
248 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700249
250 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800251 src = self.abs_src
252 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253 # copy file if it does not exist or is out of date
254 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
255 try:
256 # remove existing file first, since it might be read-only
257 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800258 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400259 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200260 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700261 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200262 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263 shutil.copy(src, dest)
264 # make the file read-only
265 mode = os.stat(dest)[stat.ST_MODE]
266 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
267 os.chmod(dest, mode)
268 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700269 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700270
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700271
Anthony King7bdac712014-07-16 12:56:40 +0100272class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700273
Wink Saville4c426ef2015-06-03 08:05:17 -0700274 def __init__(self, git_worktree, src, dest, relsrc, absdest):
275 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.src = src
277 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700278 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 self.abs_dest = absdest
280
Wink Saville4c426ef2015-06-03 08:05:17 -0700281 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500282 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700283 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500284 try:
285 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800286 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800287 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700289 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700290 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500291 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700292 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500293 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700294 _error('Cannot link file %s to %s', relSrc, absDest)
295
296 def _Link(self):
297 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
298 on the src linking all of the files in the source in to the destination
299 directory.
300 """
301 # We use the absSrc to handle the situation where the current directory
302 # is not the root of the repo
303 absSrc = os.path.join(self.git_worktree, self.src)
304 if os.path.exists(absSrc):
305 # Entity exists so just a simple one to one link operation
306 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
307 else:
308 # Entity doesn't exist assume there is a wild card
309 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700310 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700311 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700312 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700313 else:
314 absSrcFiles = glob.glob(absSrc)
315 for absSrcFile in absSrcFiles:
316 # Create a releative path from source dir to destination dir
317 absSrcDir = os.path.dirname(absSrcFile)
318 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
319
320 # Get the source file name
321 srcFile = os.path.basename(absSrcFile)
322
323 # Now form the final full paths to srcFile. They will be
324 # absolute for the desintaiton and relative for the srouce.
325 absDest = os.path.join(absDestDir, srcFile)
326 relSrc = os.path.join(relSrcDir, srcFile)
327 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500328
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700329
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700331
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700332 def __init__(self,
333 name,
Anthony King7bdac712014-07-16 12:56:40 +0100334 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700335 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100336 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700337 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700338 orig_name=None,
339 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.name = name
341 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700342 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700343 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100344 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700345 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700346 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700347
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700348
Doug Anderson37282b42011-03-04 11:54:18 -0800349class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700350
Doug Anderson37282b42011-03-04 11:54:18 -0800351 """A RepoHook contains information about a script to run as a hook.
352
353 Hooks are used to run a python script before running an upload (for instance,
354 to run presubmit checks). Eventually, we may have hooks for other actions.
355
356 This shouldn't be confused with files in the 'repo/hooks' directory. Those
357 files are copied into each '.git/hooks' folder for each project. Repo-level
358 hooks are associated instead with repo actions.
359
360 Hooks are always python. When a hook is run, we will load the hook into the
361 interpreter and execute its main() function.
362 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700363
Doug Anderson37282b42011-03-04 11:54:18 -0800364 def __init__(self,
365 hook_type,
366 hooks_project,
367 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400368 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800369 abort_if_user_denies=False):
370 """RepoHook constructor.
371
372 Params:
373 hook_type: A string representing the type of hook. This is also used
374 to figure out the name of the file containing the hook. For
375 example: 'pre-upload'.
376 hooks_project: The project containing the repo hooks. If you have a
377 manifest, this is manifest.repo_hooks_project. OK if this is None,
378 which will make the hook a no-op.
379 topdir: Repo's top directory (the one containing the .repo directory).
380 Scripts will run with CWD as this directory. If you have a manifest,
381 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400382 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800383 abort_if_user_denies: If True, we'll throw a HookError() if the user
384 doesn't allow us to run the hook.
385 """
386 self._hook_type = hook_type
387 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400388 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800389 self._topdir = topdir
390 self._abort_if_user_denies = abort_if_user_denies
391
392 # Store the full path to the script for convenience.
393 if self._hooks_project:
394 self._script_fullpath = os.path.join(self._hooks_project.worktree,
395 self._hook_type + '.py')
396 else:
397 self._script_fullpath = None
398
399 def _GetHash(self):
400 """Return a hash of the contents of the hooks directory.
401
402 We'll just use git to do this. This hash has the property that if anything
403 changes in the directory we will return a different has.
404
405 SECURITY CONSIDERATION:
406 This hash only represents the contents of files in the hook directory, not
407 any other files imported or called by hooks. Changes to imported files
408 can change the script behavior without affecting the hash.
409
410 Returns:
411 A string representing the hash. This will always be ASCII so that it can
412 be printed to the user easily.
413 """
414 assert self._hooks_project, "Must have hooks to calculate their hash."
415
416 # We will use the work_git object rather than just calling GetRevisionId().
417 # That gives us a hash of the latest checked in version of the files that
418 # the user will actually be executing. Specifically, GetRevisionId()
419 # doesn't appear to change even if a user checks out a different version
420 # of the hooks repo (via git checkout) nor if a user commits their own revs.
421 #
422 # NOTE: Local (non-committed) changes will not be factored into this hash.
423 # I think this is OK, since we're really only worried about warning the user
424 # about upstream changes.
425 return self._hooks_project.work_git.rev_parse('HEAD')
426
427 def _GetMustVerb(self):
428 """Return 'must' if the hook is required; 'should' if not."""
429 if self._abort_if_user_denies:
430 return 'must'
431 else:
432 return 'should'
433
434 def _CheckForHookApproval(self):
435 """Check to see whether this hook has been approved.
436
Mike Frysinger40252c22016-08-15 21:23:44 -0400437 We'll accept approval of manifest URLs if they're using secure transports.
438 This way the user can say they trust the manifest hoster. For insecure
439 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800440
441 Note that we ask permission for each individual hook even though we use
442 the hash of all hooks when detecting changes. We'd like the user to be
443 able to approve / deny each hook individually. We only use the hash of all
444 hooks because there is no other easy way to detect changes to local imports.
445
446 Returns:
447 True if this hook is approved to run; False otherwise.
448
449 Raises:
450 HookError: Raised if the user doesn't approve and abort_if_user_denies
451 was passed to the consturctor.
452 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400453 if self._ManifestUrlHasSecureScheme():
454 return self._CheckForHookApprovalManifest()
455 else:
456 return self._CheckForHookApprovalHash()
457
458 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
459 changed_prompt):
460 """Check for approval for a particular attribute and hook.
461
462 Args:
463 subkey: The git config key under [repo.hooks.<hook_type>] to store the
464 last approved string.
465 new_val: The new value to compare against the last approved one.
466 main_prompt: Message to display to the user to ask for approval.
467 changed_prompt: Message explaining why we're re-asking for approval.
468
469 Returns:
470 True if this hook is approved to run; False otherwise.
471
472 Raises:
473 HookError: Raised if the user doesn't approve and abort_if_user_denies
474 was passed to the consturctor.
475 """
Doug Anderson37282b42011-03-04 11:54:18 -0800476 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 # Get the last value that the user approved for this hook; may be None.
480 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800481
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800483 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400484 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800485 # Approval matched. We're done.
486 return True
487 else:
488 # Give the user a reason why we're prompting, since they last told
489 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400490 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800491 else:
492 prompt = ''
493
494 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
495 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400496 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530497 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900498 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800499
500 # User is doing a one-time approval.
501 if response in ('y', 'yes'):
502 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400503 elif response == 'always':
504 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800505 return True
506
507 # For anything else, we'll assume no approval.
508 if self._abort_if_user_denies:
509 raise HookError('You must allow the %s hook or use --no-verify.' %
510 self._hook_type)
511
512 return False
513
Mike Frysinger40252c22016-08-15 21:23:44 -0400514 def _ManifestUrlHasSecureScheme(self):
515 """Check if the URI for the manifest is a secure transport."""
516 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
517 parse_results = urllib.parse.urlparse(self._manifest_url)
518 return parse_results.scheme in secure_schemes
519
520 def _CheckForHookApprovalManifest(self):
521 """Check whether the user has approved this manifest host.
522
523 Returns:
524 True if this hook is approved to run; False otherwise.
525 """
526 return self._CheckForHookApprovalHelper(
527 'approvedmanifest',
528 self._manifest_url,
529 'Run hook scripts from %s' % (self._manifest_url,),
530 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
531
532 def _CheckForHookApprovalHash(self):
533 """Check whether the user has approved the hooks repo.
534
535 Returns:
536 True if this hook is approved to run; False otherwise.
537 """
538 prompt = ('Repo %s run the script:\n'
539 ' %s\n'
540 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700541 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400542 return self._CheckForHookApprovalHelper(
543 'approvedhash',
544 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700545 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400546 'Scripts have changed since %s was allowed.' % (self._hook_type,))
547
Doug Anderson37282b42011-03-04 11:54:18 -0800548 def _ExecuteHook(self, **kwargs):
549 """Actually execute the given hook.
550
551 This will run the hook's 'main' function in our python interpreter.
552
553 Args:
554 kwargs: Keyword arguments to pass to the hook. These are often specific
555 to the hook type. For instance, pre-upload hooks will contain
556 a project_list.
557 """
558 # Keep sys.path and CWD stashed away so that we can always restore them
559 # upon function exit.
560 orig_path = os.getcwd()
561 orig_syspath = sys.path
562
563 try:
564 # Always run hooks with CWD as topdir.
565 os.chdir(self._topdir)
566
567 # Put the hook dir as the first item of sys.path so hooks can do
568 # relative imports. We want to replace the repo dir as [0] so
569 # hooks can't import repo files.
570 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
571
572 # Exec, storing global context in the context dict. We catch exceptions
573 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500574 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800575 try:
Anthony King70f68902014-05-05 21:15:34 +0100576 exec(compile(open(self._script_fullpath).read(),
577 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800578 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700579 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
580 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800581
582 # Running the script should have defined a main() function.
583 if 'main' not in context:
584 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
585
Doug Anderson37282b42011-03-04 11:54:18 -0800586 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
587 # We don't actually want hooks to define their main with this argument--
588 # it's there to remind them that their hook should always take **kwargs.
589 # For instance, a pre-upload hook should be defined like:
590 # def main(project_list, **kwargs):
591 #
592 # This allows us to later expand the API without breaking old hooks.
593 kwargs = kwargs.copy()
594 kwargs['hook_should_take_kwargs'] = True
595
596 # Call the main function in the hook. If the hook should cause the
597 # build to fail, it will raise an Exception. We'll catch that convert
598 # to a HookError w/ just the failing traceback.
599 try:
600 context['main'](**kwargs)
601 except Exception:
602 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700603 'above.' % (traceback.format_exc(),
604 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800605 finally:
606 # Restore sys.path and CWD.
607 sys.path = orig_syspath
608 os.chdir(orig_path)
609
610 def Run(self, user_allows_all_hooks, **kwargs):
611 """Run the hook.
612
613 If the hook doesn't exist (because there is no hooks project or because
614 this particular hook is not enabled), this is a no-op.
615
616 Args:
617 user_allows_all_hooks: If True, we will never prompt about running the
618 hook--we'll just assume it's OK to run it.
619 kwargs: Keyword arguments to pass to the hook. These are often specific
620 to the hook type. For instance, pre-upload hooks will contain
621 a project_list.
622
623 Raises:
624 HookError: If there was a problem finding the hook or the user declined
625 to run a required hook (from _CheckForHookApproval).
626 """
627 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700628 if ((not self._hooks_project) or (self._hook_type not in
629 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800630 return
631
632 # Bail with a nice error if we can't find the hook.
633 if not os.path.isfile(self._script_fullpath):
634 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
635
636 # Make sure the user is OK with running the hook.
637 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
638 return
639
640 # Run the hook with the same version of python we're using.
641 self._ExecuteHook(**kwargs)
642
643
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700644class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600645 # These objects can be shared between several working trees.
646 shareable_files = ['description', 'info']
647 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
648 # These objects can only be used by a single working tree.
649 working_tree_files = ['config', 'packed-refs', 'shallow']
650 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700651
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700652 def __init__(self,
653 manifest,
654 name,
655 remote,
656 gitdir,
David James8d201162013-10-11 17:03:19 -0700657 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700658 worktree,
659 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700660 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800661 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100662 rebase=True,
663 groups=None,
664 sync_c=False,
665 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900666 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100667 clone_depth=None,
668 upstream=None,
669 parent=None,
670 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900671 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700672 optimized_fetch=False,
673 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800674 """Init a Project object.
675
676 Args:
677 manifest: The XmlManifest object.
678 name: The `name` attribute of manifest.xml's project element.
679 remote: RemoteSpec object specifying its remote's properties.
680 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700681 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800682 worktree: Absolute path of git working tree.
683 relpath: Relative path of git working tree to repo's top directory.
684 revisionExpr: The `revision` attribute of manifest.xml's project element.
685 revisionId: git commit id for checking out.
686 rebase: The `rebase` attribute of manifest.xml's project element.
687 groups: The `groups` attribute of manifest.xml's project element.
688 sync_c: The `sync-c` attribute of manifest.xml's project element.
689 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900690 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800691 upstream: The `upstream` attribute of manifest.xml's project element.
692 parent: The parent Project object.
693 is_derived: False if the project was explicitly defined in the manifest;
694 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400695 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900696 optimized_fetch: If True, when a project is set to a sha1 revision, only
697 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700698 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800699 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 self.manifest = manifest
701 self.name = name
702 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800703 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700704 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800705 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700706 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800707 else:
708 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700709 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700710 self.revisionExpr = revisionExpr
711
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700712 if revisionId is None \
713 and revisionExpr \
714 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700715 self.revisionId = revisionExpr
716 else:
717 self.revisionId = revisionId
718
Mike Pontillod3153822012-02-28 11:53:24 -0800719 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700720 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700721 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800722 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900723 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900724 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700725 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800726 self.parent = parent
727 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900728 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800729 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800730
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700731 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500733 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500734 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700735 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
736 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800738 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700739 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800740 else:
741 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700742 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700743 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700744 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400745 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700746 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747
Doug Anderson37282b42011-03-04 11:54:18 -0800748 # This will be filled in if a project is later identified to be the
749 # project containing repo hooks.
750 self.enabled_repo_hooks = []
751
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700752 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800753 def Derived(self):
754 return self.is_derived
755
756 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700758 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700759
760 @property
761 def CurrentBranch(self):
762 """Obtain the name of the currently checked out branch.
763 The branch name omits the 'refs/heads/' prefix.
764 None is returned if the project is on a detached HEAD.
765 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700766 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700767 if b.startswith(R_HEADS):
768 return b[len(R_HEADS):]
769 return None
770
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700771 def IsRebaseInProgress(self):
772 w = self.worktree
773 g = os.path.join(w, '.git')
774 return os.path.exists(os.path.join(g, 'rebase-apply')) \
775 or os.path.exists(os.path.join(g, 'rebase-merge')) \
776 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200777
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700778 def IsDirty(self, consider_untracked=True):
779 """Is the working directory modified in some way?
780 """
781 self.work_git.update_index('-q',
782 '--unmerged',
783 '--ignore-missing',
784 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900785 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700786 return True
787 if self.work_git.DiffZ('diff-files'):
788 return True
789 if consider_untracked and self.work_git.LsOthers():
790 return True
791 return False
792
793 _userident_name = None
794 _userident_email = None
795
796 @property
797 def UserName(self):
798 """Obtain the user's personal name.
799 """
800 if self._userident_name is None:
801 self._LoadUserIdentity()
802 return self._userident_name
803
804 @property
805 def UserEmail(self):
806 """Obtain the user's email address. This is very likely
807 to be their Gerrit login.
808 """
809 if self._userident_email is None:
810 self._LoadUserIdentity()
811 return self._userident_email
812
813 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900814 u = self.bare_git.var('GIT_COMMITTER_IDENT')
815 m = re.compile("^(.*) <([^>]*)> ").match(u)
816 if m:
817 self._userident_name = m.group(1)
818 self._userident_email = m.group(2)
819 else:
820 self._userident_name = ''
821 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700822
823 def GetRemote(self, name):
824 """Get the configuration for a single remote.
825 """
826 return self.config.GetRemote(name)
827
828 def GetBranch(self, name):
829 """Get the configuration for a single branch.
830 """
831 return self.config.GetBranch(name)
832
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700833 def GetBranches(self):
834 """Get all existing local branches.
835 """
836 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900837 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700838 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700839
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530840 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700841 if name.startswith(R_HEADS):
842 name = name[len(R_HEADS):]
843 b = self.GetBranch(name)
844 b.current = name == current
845 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900846 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700847 heads[name] = b
848
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530849 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700850 if name.startswith(R_PUB):
851 name = name[len(R_PUB):]
852 b = heads.get(name)
853 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900854 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700855
856 return heads
857
Colin Cross5acde752012-03-28 20:15:45 -0700858 def MatchesGroups(self, manifest_groups):
859 """Returns true if the manifest groups specified at init should cause
860 this project to be synced.
861 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700862 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700863
864 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700865 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700866 manifest_groups: "-group1,group2"
867 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500868
869 The special manifest group "default" will match any project that
870 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700871 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500872 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700873 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700874 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500875 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876
Conley Owens971de8e2012-04-16 10:36:08 -0700877 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700878 for group in expanded_manifest_groups:
879 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700880 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700881 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700882 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700883
Conley Owens971de8e2012-04-16 10:36:08 -0700884 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700885
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700886# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700887 def UncommitedFiles(self, get_all=True):
888 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700889
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700890 Args:
891 get_all: a boolean, if True - get information about all different
892 uncommitted files. If False - return as soon as any kind of
893 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700895 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500896 self.work_git.update_index('-q',
897 '--unmerged',
898 '--ignore-missing',
899 '--refresh')
900 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700901 details.append("rebase in progress")
902 if not get_all:
903 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500904
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700905 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
906 if changes:
907 details.extend(changes)
908 if not get_all:
909 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500910
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700911 changes = self.work_git.DiffZ('diff-files').keys()
912 if changes:
913 details.extend(changes)
914 if not get_all:
915 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500916
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700917 changes = self.work_git.LsOthers()
918 if changes:
919 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500920
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700921 return details
922
923 def HasChanges(self):
924 """Returns true if there are uncommitted changes.
925 """
926 if self.UncommitedFiles(get_all=False):
927 return True
928 else:
929 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500930
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600931 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200933
934 Args:
935 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600936 quiet: If True then only print the project name. Do not print
937 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700938 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700939 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700940 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200941 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700942 print(file=output_redir)
943 print('project %s/' % self.relpath, file=output_redir)
944 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945 return
946
947 self.work_git.update_index('-q',
948 '--unmerged',
949 '--ignore-missing',
950 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700951 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
953 df = self.work_git.DiffZ('diff-files')
954 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100955 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700956 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957
958 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700959 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200960 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700961 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700962
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600963 if quiet:
964 out.nl()
965 return 'DIRTY'
966
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700967 branch = self.CurrentBranch
968 if branch is None:
969 out.nobranch('(*** NO BRANCH ***)')
970 else:
971 out.branch('branch %s', branch)
972 out.nl()
973
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700974 if rb:
975 out.important('prior sync failed; rebase still in progress')
976 out.nl()
977
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978 paths = list()
979 paths.extend(di.keys())
980 paths.extend(df.keys())
981 paths.extend(do)
982
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530983 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 try:
985 i = di[p]
986 except KeyError:
987 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 try:
990 f = df[p]
991 except KeyError:
992 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200993
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900994 if i:
995 i_status = i.status.upper()
996 else:
997 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700998
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900999 if f:
1000 f_status = f.status.lower()
1001 else:
1002 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001003
1004 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001005 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001006 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001007 else:
1008 line = ' %s%s\t%s' % (i_status, f_status, p)
1009
1010 if i and not f:
1011 out.added('%s', line)
1012 elif (i and f) or (not i and f):
1013 out.changed('%s', line)
1014 elif not i and not f:
1015 out.untracked('%s', line)
1016 else:
1017 out.write('%s', line)
1018 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001019
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001020 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021
pelyad67872d2012-03-28 14:49:58 +03001022 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023 """Prints the status of the repository to stdout.
1024 """
1025 out = DiffColoring(self.config)
1026 cmd = ['diff']
1027 if out.is_on:
1028 cmd.append('--color')
1029 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001030 if absolute_paths:
1031 cmd.append('--src-prefix=a/%s/' % self.relpath)
1032 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 cmd.append('--')
1034 p = GitCommand(self,
1035 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001036 capture_stdout=True,
1037 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001038 has_diff = False
1039 for line in p.process.stdout:
1040 if not has_diff:
1041 out.nl()
1042 out.project('project %s/' % self.relpath)
1043 out.nl()
1044 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001045 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001046 p.Wait()
1047
1048
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001049# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050
David Pursehouse8a68ff92012-09-24 12:15:13 +09001051 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052 """Was the branch published (uploaded) for code review?
1053 If so, returns the SHA-1 hash of the last published
1054 state for the branch.
1055 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001056 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001057 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001058 try:
1059 return self.bare_git.rev_parse(key)
1060 except GitError:
1061 return None
1062 else:
1063 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001064 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001065 except KeyError:
1066 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067
David Pursehouse8a68ff92012-09-24 12:15:13 +09001068 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 """Prunes any stale published refs.
1070 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001071 if all_refs is None:
1072 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001073 heads = set()
1074 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301075 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001076 if name.startswith(R_HEADS):
1077 heads.add(name)
1078 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001079 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301081 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 n = name[len(R_PUB):]
1083 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001084 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001086 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087 """List any branches which can be uploaded for review.
1088 """
1089 heads = {}
1090 pubed = {}
1091
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301092 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001093 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001094 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001096 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001097
1098 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301099 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001100 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001102 if selected_branch and branch != selected_branch:
1103 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001104
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001105 rb = self.GetUploadableBranch(branch)
1106 if rb:
1107 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001108 return ready
1109
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001110 def GetUploadableBranch(self, branch_name):
1111 """Get a single uploadable branch, or None.
1112 """
1113 branch = self.GetBranch(branch_name)
1114 base = branch.LocalMerge
1115 if branch.LocalMerge:
1116 rb = ReviewableBranch(self, branch, base)
1117 if rb.commits:
1118 return rb
1119 return None
1120
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001121 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001122 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001123 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001124 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001125 private=False,
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001126 notify=None,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001127 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001128 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001129 validate_certs=True,
1130 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001131 """Uploads the named branch for code review.
1132 """
1133 if branch is None:
1134 branch = self.CurrentBranch
1135 if branch is None:
1136 raise GitError('not currently on a branch')
1137
1138 branch = self.GetBranch(branch)
1139 if not branch.LocalMerge:
1140 raise GitError('branch %s does not track a remote' % branch.name)
1141 if not branch.remote.review:
1142 raise GitError('remote %s has no review url' % branch.remote.name)
1143
Bryan Jacobsf609f912013-05-06 13:36:24 -04001144 if dest_branch is None:
1145 dest_branch = self.dest_branch
1146 if dest_branch is None:
1147 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001148 if not dest_branch.startswith(R_HEADS):
1149 dest_branch = R_HEADS + dest_branch
1150
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001151 if not branch.remote.projectname:
1152 branch.remote.projectname = self.name
1153 branch.remote.Save()
1154
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001155 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001156 if url is None:
1157 raise UploadError('review not configured')
1158 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001159
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001160 if url.startswith('ssh://'):
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001161 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001162
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001163 for push_option in (push_options or []):
1164 cmd.append('-o')
1165 cmd.append(push_option)
1166
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001168
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001169 if dest_branch.startswith(R_HEADS):
1170 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001171
1172 upload_type = 'for'
1173 if draft:
1174 upload_type = 'drafts'
1175
1176 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1177 dest_branch)
David Pursehousef25a3702018-11-14 19:01:22 -08001178 opts = []
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001179 if auto_topic:
David Pursehousef25a3702018-11-14 19:01:22 -08001180 opts += ['topic=' + branch.name]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001181
David Pursehousef25a3702018-11-14 19:01:22 -08001182 opts += ['r=%s' % p for p in people[0]]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001183 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury75bcd242018-10-31 13:48:01 -07001184 if notify:
1185 opts += ['notify=' + notify]
Jonathan Nieder81df7e12018-11-05 13:21:52 -08001186 if private:
1187 opts += ['private']
1188 if wip:
1189 opts += ['wip']
1190 if opts:
1191 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001192 cmd.append(ref_spec)
1193
Anthony King7bdac712014-07-16 12:56:40 +01001194 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001195 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001196
1197 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1198 self.bare_git.UpdateRef(R_PUB + branch.name,
1199 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001200 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
1202
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001203# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001204
Julien Campergue335f5ef2013-10-16 11:02:35 +02001205 def _ExtractArchive(self, tarpath, path=None):
1206 """Extract the given tar on its current location
1207
1208 Args:
1209 - tarpath: The path to the actual tar file
1210
1211 """
1212 try:
1213 with tarfile.open(tarpath, 'r') as tar:
1214 tar.extractall(path=path)
1215 return True
1216 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001217 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001218 return False
1219
Ningning Xiac2fbc782016-08-22 14:24:39 -07001220 def CachePopulate(self, cache_dir, url):
1221 """Populate cache in the cache_dir.
1222
1223 Args:
1224 cache_dir: Directory to cache git files from Google Storage.
1225 url: Git url of current repository.
1226
1227 Raises:
1228 CacheApplyError if it fails to populate the git cache.
1229 """
1230 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1231 '--cache-dir', cache_dir, url]
1232
1233 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1234 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1235 'url: %s' % (cache_dir, url))
1236
1237 def CacheExists(self, cache_dir, url):
1238 """Check the existence of the cache files.
1239
1240 Args:
1241 cache_dir: Directory to cache git files.
1242 url: Git url of current repository.
1243
1244 Raises:
1245 CacheApplyError if the cache files do not exist.
1246 """
1247 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1248
1249 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1250 if exist.Wait() != 0:
1251 raise CacheApplyError('Failed to execute git cache exists cmd. '
1252 'cache_dir: %s url: %s' % (cache_dir, url))
1253
1254 if not exist.stdout or not exist.stdout.strip():
1255 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1256 'url: %s' % (cache_dir, url))
1257 return exist.stdout.strip()
1258
1259 def CacheApply(self, cache_dir):
1260 """Apply git cache files populated from Google Storage buckets.
1261
1262 Args:
1263 cache_dir: Directory to cache git files.
1264
1265 Raises:
1266 CacheApplyError if it fails to apply git caches.
1267 """
1268 remote = self.GetRemote(self.remote.name)
1269
1270 self.CachePopulate(cache_dir, remote.url)
1271
1272 mirror_dir = self.CacheExists(cache_dir, remote.url)
1273
1274 refspec = RefSpec(True, 'refs/heads/*',
1275 'refs/remotes/%s/*' % remote.name)
1276
1277 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1278 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1279 raise CacheApplyError('Failed to fetch refs %s from %s' %
1280 (mirror_dir, str(refspec)))
1281
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001282 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001283 quiet=False,
1284 is_new=None,
1285 current_branch_only=False,
1286 force_sync=False,
1287 clone_bundle=True,
1288 no_tags=False,
1289 archive=False,
1290 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001291 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001292 submodules=False,
Mike Frysinger39ba6312019-07-27 12:45:51 -04001293 clone_filter=None,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001294 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001295 """Perform only the network IO portion of the sync process.
1296 Local working directory/branch state is not affected.
1297 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001298 if archive and not isinstance(self, MetaProject):
1299 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001300 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001301 return False
1302
1303 name = self.relpath.replace('\\', '/')
1304 name = name.replace('/', '_')
1305 tarpath = '%s.tar' % name
1306 topdir = self.manifest.topdir
1307
1308 try:
1309 self._FetchArchive(tarpath, cwd=topdir)
1310 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001311 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001312 return False
1313
1314 # From now on, we only need absolute tarpath
1315 tarpath = os.path.join(topdir, tarpath)
1316
1317 if not self._ExtractArchive(tarpath, path=topdir):
1318 return False
1319 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001320 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001321 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001322 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001323 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001324 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001325 if is_new is None:
1326 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001327 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001328 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001329 else:
1330 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001331 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001332
1333 if is_new:
1334 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1335 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001336 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001337 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001338 # This works for both absolute and relative alternate directories.
1339 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001340 finally:
1341 fd.close()
1342 except IOError:
1343 alt_dir = None
1344 else:
1345 alt_dir = None
1346
Ningning Xiac2fbc782016-08-22 14:24:39 -07001347 applied_cache = False
1348 # If cache_dir is provided, and it's a new repository without
1349 # alternative_dir, bootstrap this project repo with the git
1350 # cache files.
1351 if cache_dir is not None and is_new and alt_dir is None:
1352 try:
1353 self.CacheApply(cache_dir)
1354 applied_cache = True
1355 is_new = False
1356 except CacheApplyError as e:
1357 _error('Could not apply git cache: %s', e)
1358 _error('Please check if you have the right GS credentials.')
1359 _error('Please check if the cache files exist in GS.')
1360
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001361 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001362 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001363 and alt_dir is None \
1364 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001365 is_new = False
1366
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001367 if not current_branch_only:
1368 if self.sync_c:
1369 current_branch_only = True
1370 elif not self.manifest._loaded:
1371 # Manifest cannot check defaults until it syncs.
1372 current_branch_only = False
1373 elif self.manifest.default.sync_c:
1374 current_branch_only = True
1375
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001376 if not no_tags:
1377 if not self.sync_tags:
1378 no_tags = True
1379
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001380 if self.clone_depth:
1381 depth = self.clone_depth
1382 else:
1383 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1384
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001385 need_to_fetch = not (optimized_fetch and
1386 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001387 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001388 if (need_to_fetch and
1389 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1390 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001391 no_tags=no_tags, prune=prune, depth=depth,
Xin Li745be2e2019-06-03 11:24:30 -07001392 submodules=submodules, force_sync=force_sync,
1393 clone_filter=clone_filter)):
Anthony King7bdac712014-07-16 12:56:40 +01001394 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001395
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001396 mp = self.manifest.manifestProject
1397 dissociate = mp.config.GetBoolean('repo.dissociate')
1398 if dissociate:
1399 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1400 if os.path.exists(alternates_file):
1401 cmd = ['repack', '-a', '-d']
1402 if GitCommand(self, cmd, bare=True).Wait() != 0:
1403 return False
1404 platform_utils.remove(alternates_file)
1405
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001406 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001407 self._InitMRef()
1408 else:
1409 self._InitMirrorHead()
1410 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001411 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001412 except OSError:
1413 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001414 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001415
1416 def PostRepoUpgrade(self):
1417 self._InitHooks()
1418
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001419 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001420 if self.manifest.isGitcClient:
1421 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001422 for copyfile in self.copyfiles:
1423 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001424 for linkfile in self.linkfiles:
1425 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001426
Julien Camperguedd654222014-01-09 16:21:37 +01001427 def GetCommitRevisionId(self):
1428 """Get revisionId of a commit.
1429
1430 Use this method instead of GetRevisionId to get the id of the commit rather
1431 than the id of the current git object (for example, a tag)
1432
1433 """
1434 if not self.revisionExpr.startswith(R_TAGS):
1435 return self.GetRevisionId(self._allrefs)
1436
1437 try:
1438 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1439 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001440 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1441 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001442
David Pursehouse8a68ff92012-09-24 12:15:13 +09001443 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001444 if self.revisionId:
1445 return self.revisionId
1446
1447 rem = self.GetRemote(self.remote.name)
1448 rev = rem.ToLocal(self.revisionExpr)
1449
David Pursehouse8a68ff92012-09-24 12:15:13 +09001450 if all_refs is not None and rev in all_refs:
1451 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001452
1453 try:
1454 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1455 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001456 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1457 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001458
Martin Kellye4e94d22017-03-21 16:05:12 -07001459 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001460 """Perform only the local IO portion of the sync process.
1461 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001462 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001463 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001464 all_refs = self.bare_ref.all
1465 self.CleanPublishedCache(all_refs)
1466 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001467
David Pursehouse1d947b32012-10-25 12:23:11 +09001468 def _doff():
1469 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001470 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001471
Martin Kellye4e94d22017-03-21 16:05:12 -07001472 def _dosubmodules():
1473 self._SyncSubmodules(quiet=True)
1474
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001475 head = self.work_git.GetHead()
1476 if head.startswith(R_HEADS):
1477 branch = head[len(R_HEADS):]
1478 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001479 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001480 except KeyError:
1481 head = None
1482 else:
1483 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001484
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001485 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001486 # Currently on a detached HEAD. The user is assumed to
1487 # not have any local modifications worth worrying about.
1488 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001489 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001490 syncbuf.fail(self, _PriorSyncFailedError())
1491 return
1492
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001493 if head == revid:
1494 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001495 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001496 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001497 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001498 # The copy/linkfile config may have changed.
1499 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001500 return
1501 else:
1502 lost = self._revlist(not_rev(revid), HEAD)
1503 if lost:
1504 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001505
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001507 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001508 if submodules:
1509 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001510 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001511 syncbuf.fail(self, e)
1512 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001513 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001514 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001516 if head == revid:
1517 # No changes; don't do anything further.
1518 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001519 # The copy/linkfile config may have changed.
1520 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001521 return
1522
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001525 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001526 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001527 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001528 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001529 syncbuf.info(self,
1530 "leaving %s; does not track upstream",
1531 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001532 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001533 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001534 if submodules:
1535 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001536 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001537 syncbuf.fail(self, e)
1538 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001539 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001540 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001542 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001543 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001545 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001546 if not_merged:
1547 if upstream_gain:
1548 # The user has published this branch and some of those
1549 # commits are not yet merged upstream. We do not want
1550 # to rewrite the published commits so we punt.
1551 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001552 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001553 "branch %s is published (but not merged) and is now "
1554 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001555 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001556 elif pub == head:
1557 # All published commits are merged, and thus we are a
1558 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001559 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001560 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001561 if submodules:
1562 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001563 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001564
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001565 # Examine the local commits not in the remote. Find the
1566 # last one attributed to this user, if any.
1567 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001568 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001569 last_mine = None
1570 cnt_mine = 0
1571 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301572 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001573 if committer_email == self.UserEmail:
1574 last_mine = commit_id
1575 cnt_mine += 1
1576
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001577 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001578 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001579
1580 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001581 syncbuf.fail(self, _DirtyError())
1582 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001583
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001584 # If the upstream switched on us, warn the user.
1585 #
1586 if branch.merge != self.revisionExpr:
1587 if branch.merge and self.revisionExpr:
1588 syncbuf.info(self,
1589 'manifest switched %s...%s',
1590 branch.merge,
1591 self.revisionExpr)
1592 elif branch.merge:
1593 syncbuf.info(self,
1594 'manifest no longer tracks %s',
1595 branch.merge)
1596
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001597 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001598 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001599 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001600 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001601 syncbuf.info(self,
1602 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001603 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001604
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001605 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001606 if not ID_RE.match(self.revisionExpr):
1607 # in case of manifest sync the revisionExpr might be a SHA1
1608 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001609 if not branch.merge.startswith('refs/'):
1610 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001611 branch.Save()
1612
Mike Pontillod3153822012-02-28 11:53:24 -08001613 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001614 def _docopyandlink():
1615 self._CopyAndLinkFiles()
1616
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001617 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001618 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001619 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001620 if submodules:
1621 syncbuf.later2(self, _dosubmodules)
1622 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001623 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001624 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001625 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001626 if submodules:
1627 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001628 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001629 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001630 syncbuf.fail(self, e)
1631 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001632 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001633 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001634 if submodules:
1635 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001636
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001637 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638 # dest should already be an absolute path, but src is project relative
1639 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001640 abssrc = os.path.join(self.worktree, src)
1641 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001642
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001643 def AddLinkFile(self, src, dest, absdest):
1644 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001645 # make src relative path to dest
1646 absdestdir = os.path.dirname(absdest)
1647 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001648 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001649
James W. Mills24c13082012-04-12 15:04:13 -05001650 def AddAnnotation(self, name, value, keep):
1651 self.annotations.append(_Annotation(name, value, keep))
1652
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001653 def DownloadPatchSet(self, change_id, patch_id):
1654 """Download a single patch set of a single change to FETCH_HEAD.
1655 """
1656 remote = self.GetRemote(self.remote.name)
1657
1658 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001659 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001660 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001661 if GitCommand(self, cmd, bare=True).Wait() != 0:
1662 return None
1663 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001664 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001665 change_id,
1666 patch_id,
1667 self.bare_git.rev_parse('FETCH_HEAD'))
1668
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001669
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001670# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001671
Simran Basib9a1b732015-08-20 12:19:28 -07001672 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001673 """Create a new branch off the manifest's revision.
1674 """
Simran Basib9a1b732015-08-20 12:19:28 -07001675 if not branch_merge:
1676 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001677 head = self.work_git.GetHead()
1678 if head == (R_HEADS + name):
1679 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001680
David Pursehouse8a68ff92012-09-24 12:15:13 +09001681 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001682 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001683 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001684 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001685 capture_stdout=True,
1686 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001687
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001688 branch = self.GetBranch(name)
1689 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001690 branch.merge = branch_merge
1691 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1692 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001693 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001694
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001695 if head.startswith(R_HEADS):
1696 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001697 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001698 except KeyError:
1699 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001700 if revid and head and revid == head:
1701 ref = os.path.join(self.gitdir, R_HEADS + name)
1702 try:
1703 os.makedirs(os.path.dirname(ref))
1704 except OSError:
1705 pass
1706 _lwrite(ref, '%s\n' % revid)
1707 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1708 'ref: %s%s\n' % (R_HEADS, name))
1709 branch.Save()
1710 return True
1711
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001712 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001713 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001714 capture_stdout=True,
1715 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001716 branch.Save()
1717 return True
1718 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001719
Wink Saville02d79452009-04-10 13:01:24 -07001720 def CheckoutBranch(self, name):
1721 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001722
1723 Args:
1724 name: The name of the branch to checkout.
1725
1726 Returns:
1727 True if the checkout succeeded; False if it didn't; None if the branch
1728 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001729 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001730 rev = R_HEADS + name
1731 head = self.work_git.GetHead()
1732 if head == rev:
1733 # Already on the branch
1734 #
1735 return True
Wink Saville02d79452009-04-10 13:01:24 -07001736
David Pursehouse8a68ff92012-09-24 12:15:13 +09001737 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001738 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001739 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001740 except KeyError:
1741 # Branch does not exist in this project
1742 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001743 return None
Wink Saville02d79452009-04-10 13:01:24 -07001744
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001745 if head.startswith(R_HEADS):
1746 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001747 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001748 except KeyError:
1749 head = None
1750
1751 if head == revid:
1752 # Same revision; just update HEAD to point to the new
1753 # target branch, but otherwise take no other action.
1754 #
1755 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1756 'ref: %s%s\n' % (R_HEADS, name))
1757 return True
1758
1759 return GitCommand(self,
1760 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001761 capture_stdout=True,
1762 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001763
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001764 def AbandonBranch(self, name):
1765 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001766
1767 Args:
1768 name: The name of the branch to abandon.
1769
1770 Returns:
1771 True if the abandon succeeded; False if it didn't; None if the branch
1772 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001773 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001774 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001775 all_refs = self.bare_ref.all
1776 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001777 # Doesn't exist
1778 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001779
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001780 head = self.work_git.GetHead()
1781 if head == rev:
1782 # We can't destroy the branch while we are sitting
1783 # on it. Switch to a detached HEAD.
1784 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001785 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001786
David Pursehouse8a68ff92012-09-24 12:15:13 +09001787 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001788 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001789 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1790 '%s\n' % revid)
1791 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001792 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001793
1794 return GitCommand(self,
1795 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001796 capture_stdout=True,
1797 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001798
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001799 def PruneHeads(self):
1800 """Prune any topic branches already merged into upstream.
1801 """
1802 cb = self.CurrentBranch
1803 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001804 left = self._allrefs
1805 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001806 if name.startswith(R_HEADS):
1807 name = name[len(R_HEADS):]
1808 if cb is None or name != cb:
1809 kill.append(name)
1810
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001811 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001812 if cb is not None \
1813 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001814 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001815 self.work_git.DetachHead(HEAD)
1816 kill.append(cb)
1817
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001818 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001819 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001820
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001821 try:
1822 self.bare_git.DetachHead(rev)
1823
1824 b = ['branch', '-d']
1825 b.extend(kill)
1826 b = GitCommand(self, b, bare=True,
1827 capture_stdout=True,
1828 capture_stderr=True)
1829 b.Wait()
1830 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001831 if ID_RE.match(old):
1832 self.bare_git.DetachHead(old)
1833 else:
1834 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001835 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001836
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001837 for branch in kill:
1838 if (R_HEADS + branch) not in left:
1839 self.CleanPublishedCache()
1840 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001841
1842 if cb and cb not in kill:
1843 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001844 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001845
1846 kept = []
1847 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001848 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001849 branch = self.GetBranch(branch)
1850 base = branch.LocalMerge
1851 if not base:
1852 base = rev
1853 kept.append(ReviewableBranch(self, branch, base))
1854 return kept
1855
1856
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001857# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001858
1859 def GetRegisteredSubprojects(self):
1860 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001861
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001862 def rec(subprojects):
1863 if not subprojects:
1864 return
1865 result.extend(subprojects)
1866 for p in subprojects:
1867 rec(p.subprojects)
1868 rec(self.subprojects)
1869 return result
1870
1871 def _GetSubmodules(self):
1872 # Unfortunately we cannot call `git submodule status --recursive` here
1873 # because the working tree might not exist yet, and it cannot be used
1874 # without a working tree in its current implementation.
1875
1876 def get_submodules(gitdir, rev):
1877 # Parse .gitmodules for submodule sub_paths and sub_urls
1878 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1879 if not sub_paths:
1880 return []
1881 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1882 # revision of submodule repository
1883 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1884 submodules = []
1885 for sub_path, sub_url in zip(sub_paths, sub_urls):
1886 try:
1887 sub_rev = sub_revs[sub_path]
1888 except KeyError:
1889 # Ignore non-exist submodules
1890 continue
1891 submodules.append((sub_rev, sub_path, sub_url))
1892 return submodules
1893
Sebastian Schuberth41a26832019-03-11 13:53:38 +01001894 re_path = re.compile(r'^submodule\.(.+)\.path=(.*)$')
1895 re_url = re.compile(r'^submodule\.(.+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001896
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001897 def parse_gitmodules(gitdir, rev):
1898 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1899 try:
Anthony King7bdac712014-07-16 12:56:40 +01001900 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1901 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001902 except GitError:
1903 return [], []
1904 if p.Wait() != 0:
1905 return [], []
1906
1907 gitmodules_lines = []
1908 fd, temp_gitmodules_path = tempfile.mkstemp()
1909 try:
1910 os.write(fd, p.stdout)
1911 os.close(fd)
1912 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001913 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1914 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001915 if p.Wait() != 0:
1916 return [], []
1917 gitmodules_lines = p.stdout.split('\n')
1918 except GitError:
1919 return [], []
1920 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001921 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001922
1923 names = set()
1924 paths = {}
1925 urls = {}
1926 for line in gitmodules_lines:
1927 if not line:
1928 continue
1929 m = re_path.match(line)
1930 if m:
1931 names.add(m.group(1))
1932 paths[m.group(1)] = m.group(2)
1933 continue
1934 m = re_url.match(line)
1935 if m:
1936 names.add(m.group(1))
1937 urls[m.group(1)] = m.group(2)
1938 continue
1939 names = sorted(names)
1940 return ([paths.get(name, '') for name in names],
1941 [urls.get(name, '') for name in names])
1942
1943 def git_ls_tree(gitdir, rev, paths):
1944 cmd = ['ls-tree', rev, '--']
1945 cmd.extend(paths)
1946 try:
Anthony King7bdac712014-07-16 12:56:40 +01001947 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1948 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001949 except GitError:
1950 return []
1951 if p.Wait() != 0:
1952 return []
1953 objects = {}
1954 for line in p.stdout.split('\n'):
1955 if not line.strip():
1956 continue
1957 object_rev, object_path = line.split()[2:4]
1958 objects[object_path] = object_rev
1959 return objects
1960
1961 try:
1962 rev = self.GetRevisionId()
1963 except GitError:
1964 return []
1965 return get_submodules(self.gitdir, rev)
1966
1967 def GetDerivedSubprojects(self):
1968 result = []
1969 if not self.Exists:
1970 # If git repo does not exist yet, querying its submodules will
1971 # mess up its states; so return here.
1972 return result
1973 for rev, path, url in self._GetSubmodules():
1974 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001975 relpath, worktree, gitdir, objdir = \
1976 self.manifest.GetSubprojectPaths(self, name, path)
1977 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001978 if project:
1979 result.extend(project.GetDerivedSubprojects())
1980 continue
David James8d201162013-10-11 17:03:19 -07001981
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001982 if url.startswith('..'):
1983 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001984 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001985 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001986 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001987 review=self.remote.review,
1988 revision=self.remote.revision)
1989 subproject = Project(manifest=self.manifest,
1990 name=name,
1991 remote=remote,
1992 gitdir=gitdir,
1993 objdir=objdir,
1994 worktree=worktree,
1995 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001996 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001997 revisionId=rev,
1998 rebase=self.rebase,
1999 groups=self.groups,
2000 sync_c=self.sync_c,
2001 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09002002 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01002003 parent=self,
2004 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08002005 result.append(subproject)
2006 result.extend(subproject.GetDerivedSubprojects())
2007 return result
2008
2009
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002010# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06002011 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05002012 try:
2013 # if revision (sha or tag) is not present then following function
2014 # throws an error.
2015 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
2016 return True
2017 except GitError:
2018 # There is no such persistent revision. We have to fetch it.
2019 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002020
Julien Campergue335f5ef2013-10-16 11:02:35 +02002021 def _FetchArchive(self, tarpath, cwd=None):
2022 cmd = ['archive', '-v', '-o', tarpath]
2023 cmd.append('--remote=%s' % self.remote.url)
2024 cmd.append('--prefix=%s/' % self.relpath)
2025 cmd.append(self.revisionExpr)
2026
2027 command = GitCommand(self, cmd, cwd=cwd,
2028 capture_stdout=True,
2029 capture_stderr=True)
2030
2031 if command.Wait() != 0:
2032 raise GitError('git archive %s: %s' % (self.name, command.stderr))
2033
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002034 def _RemoteFetch(self, name=None,
2035 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002036 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002037 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002038 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002039 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002040 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002041 depth=None,
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002042 submodules=False,
Xin Li745be2e2019-06-03 11:24:30 -07002043 force_sync=False,
2044 clone_filter=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002045
2046 is_sha1 = False
2047 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002048 # The depth should not be used when fetching to a mirror because
2049 # it will result in a shallow repository that cannot be cloned or
2050 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002051 # The repo project should also never be synced with partial depth.
2052 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2053 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002054
Shawn Pearce69e04d82014-01-29 12:48:54 -08002055 if depth:
2056 current_branch_only = True
2057
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002058 if ID_RE.match(self.revisionExpr) is not None:
2059 is_sha1 = True
2060
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002061 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002062 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002063 # this is a tag and its sha1 value should never change
2064 tag_name = self.revisionExpr[len(R_TAGS):]
2065
2066 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06002067 if self._CheckForImmutableRevision():
Tim Schumacher1f1596b2019-04-15 11:17:08 +02002068 if not quiet:
2069 print('Skipped fetching project %s (already have persistent ref)'
2070 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002071 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002072 if is_sha1 and not depth:
2073 # When syncing a specific commit and --depth is not set:
2074 # * if upstream is explicitly specified and is not a sha1, fetch only
2075 # upstream as users expect only upstream to be fetch.
2076 # Note: The commit might not be in upstream in which case the sync
2077 # will fail.
2078 # * otherwise, fetch all branches to make sure we end up with the
2079 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002080 if self.upstream:
2081 current_branch_only = not ID_RE.match(self.upstream)
2082 else:
2083 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002084
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002085 if not name:
2086 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002087
2088 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002089 remote = self.GetRemote(name)
2090 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002091 ssh_proxy = True
2092
Shawn O. Pearce88443382010-10-08 10:02:09 +02002093 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002094 if alt_dir and 'objects' == os.path.basename(alt_dir):
2095 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002096 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2097 remote = self.GetRemote(name)
2098
David Pursehouse8a68ff92012-09-24 12:15:13 +09002099 all_refs = self.bare_ref.all
2100 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002101 tmp = set()
2102
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302103 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002104 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002105 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002106 all_refs[r] = ref_id
2107 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002108 continue
2109
David Pursehouse8a68ff92012-09-24 12:15:13 +09002110 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111 continue
2112
David Pursehouse8a68ff92012-09-24 12:15:13 +09002113 r = 'refs/_alt/%s' % ref_id
2114 all_refs[r] = ref_id
2115 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002116 tmp.add(r)
2117
heping3d7bbc92017-04-12 19:51:47 +08002118 tmp_packed_lines = []
2119 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002120
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302121 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002122 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002123 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002124 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002125 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002126
heping3d7bbc92017-04-12 19:51:47 +08002127 tmp_packed = ''.join(tmp_packed_lines)
2128 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002129 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002130 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002131 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002132
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002133 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002134
Xin Li745be2e2019-06-03 11:24:30 -07002135 if clone_filter:
2136 git_require((2, 19, 0), fail=True, msg='partial clones')
2137 cmd.append('--filter=%s' % clone_filter)
2138 self.config.SetString('extensions.partialclone', self.remote.name)
2139
Conley Owensf97e8382015-01-21 11:12:46 -08002140 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002141 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002142 else:
2143 # If this repo has shallow objects, then we don't know which refs have
2144 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2145 # do this with projects that don't have shallow objects, since it is less
2146 # efficient.
2147 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2148 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002149
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002150 if quiet:
2151 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002152 if not self.worktree:
2153 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002154 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002155
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002156 # If using depth then we should not get all the tags since they may
2157 # be outside of the depth.
2158 if no_tags or depth:
2159 cmd.append('--no-tags')
2160 else:
2161 cmd.append('--tags')
2162
Mike Frysinger9d0e84c2019-03-18 21:27:54 -04002163 if force_sync:
2164 cmd.append('--force')
2165
David Pursehouse74cfd272015-10-14 10:50:15 +09002166 if prune:
2167 cmd.append('--prune')
2168
Martin Kellye4e94d22017-03-21 16:05:12 -07002169 if submodules:
2170 cmd.append('--recurse-submodules=on-demand')
2171
Conley Owens80b87fe2014-05-09 17:13:44 -07002172 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002173 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002174 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002175 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002176 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002177 spec.append('tag')
2178 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002179
David Pursehouse403b64e2015-04-27 10:41:33 +09002180 if not self.manifest.IsMirror:
2181 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002182 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002183 # Shallow checkout of a specific commit, fetch from that commit and not
2184 # the heads only as the commit might be deeper in the history.
2185 spec.append(branch)
2186 else:
2187 if is_sha1:
2188 branch = self.upstream
2189 if branch is not None and branch.strip():
2190 if not branch.startswith('refs/'):
2191 branch = R_HEADS + branch
2192 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002193 cmd.extend(spec)
2194
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002195 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002196 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002197 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002198 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002199 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002200 ok = True
2201 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002202 # If needed, run the 'git remote prune' the first time through the loop
2203 elif (not _i and
2204 "error:" in gitcmd.stderr and
2205 "git remote prune" in gitcmd.stderr):
2206 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002207 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002208 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002209 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002210 break
2211 continue
Brian Harring14a66742012-09-28 20:21:57 -07002212 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002213 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2214 # in sha1 mode, we just tried sync'ing from the upstream field; it
2215 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002216 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002217 elif ret < 0:
2218 # Git died with a signal, exit immediately
2219 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002220 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002221
2222 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002223 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002224 if old_packed != '':
2225 _lwrite(packed_refs, old_packed)
2226 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002227 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002228 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002229
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002230 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002231 # We just synced the upstream given branch; verify we
2232 # got what we wanted, else trigger a second run of all
2233 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002234 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002235 if current_branch_only and depth:
2236 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002237 return self._RemoteFetch(name=name,
2238 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002239 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002240 depth=None, clone_filter=clone_filter)
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002241 else:
2242 # Avoid infinite recursion: sync all branches with depth set to None
2243 return self._RemoteFetch(name=name, current_branch_only=False,
2244 initial=False, quiet=quiet, alt_dir=alt_dir,
Xin Li745be2e2019-06-03 11:24:30 -07002245 depth=None, clone_filter=clone_filter)
Brian Harring14a66742012-09-28 20:21:57 -07002246
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002247 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002248
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002249 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002250 if initial and \
2251 (self.manifest.manifestProject.config.GetString('repo.depth') or
2252 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002253 return False
2254
2255 remote = self.GetRemote(self.remote.name)
2256 bundle_url = remote.url + '/clone.bundle'
2257 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002258 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2259 'persistent-http',
2260 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002261 return False
2262
2263 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2264 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2265
2266 exist_dst = os.path.exists(bundle_dst)
2267 exist_tmp = os.path.exists(bundle_tmp)
2268
2269 if not initial and not exist_dst and not exist_tmp:
2270 return False
2271
2272 if not exist_dst:
2273 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2274 if not exist_dst:
2275 return False
2276
2277 cmd = ['fetch']
2278 if quiet:
2279 cmd.append('--quiet')
2280 if not self.worktree:
2281 cmd.append('--update-head-ok')
2282 cmd.append(bundle_dst)
2283 for f in remote.fetch:
2284 cmd.append(str(f))
Xin Li6e538442018-12-10 11:33:16 -08002285 cmd.append('+refs/tags/*:refs/tags/*')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002286
2287 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002288 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002289 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002290 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002291 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002292 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002293
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002294 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002295 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002296 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002297
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002298 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002299 if quiet:
2300 cmd += ['--silent']
2301 if os.path.exists(tmpPath):
2302 size = os.stat(tmpPath).st_size
2303 if size >= 1024:
2304 cmd += ['--continue-at', '%d' % (size,)]
2305 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002306 platform_utils.remove(tmpPath)
Xin Li3698ab72019-06-25 16:09:43 -07002307 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002308 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002309 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Xin Li3698ab72019-06-25 16:09:43 -07002310 if proxy:
2311 cmd += ['--proxy', proxy]
2312 elif 'http_proxy' in os.environ and 'darwin' == sys.platform:
2313 cmd += ['--proxy', os.environ['http_proxy']]
2314 if srcUrl.startswith('persistent-https'):
2315 srcUrl = 'http' + srcUrl[len('persistent-https'):]
2316 elif srcUrl.startswith('persistent-http'):
2317 srcUrl = 'http' + srcUrl[len('persistent-http'):]
Dave Borowitz137d0132015-01-02 11:12:54 -08002318 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002319
Dave Borowitz137d0132015-01-02 11:12:54 -08002320 if IsTrace():
2321 Trace('%s', ' '.join(cmd))
2322 try:
2323 proc = subprocess.Popen(cmd)
2324 except OSError:
2325 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002326
Dave Borowitz137d0132015-01-02 11:12:54 -08002327 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002328
Dave Borowitz137d0132015-01-02 11:12:54 -08002329 if curlret == 22:
2330 # From curl man page:
2331 # 22: HTTP page not retrieved. The requested url was not found or
2332 # returned another error with the HTTP error code being 400 or above.
2333 # This return code only appears if -f, --fail is used.
2334 if not quiet:
2335 print("Server does not provide clone.bundle; ignoring.",
2336 file=sys.stderr)
2337 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002338
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002339 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002340 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002341 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002342 return True
2343 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002344 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002345 return False
2346 else:
2347 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002348
Kris Giesingc8d882a2014-12-23 13:02:32 -08002349 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002350 try:
Pierre Tardy2b7daff2019-05-16 10:28:21 +02002351 with open(path, 'rb') as f:
2352 if f.read(16) == b'# v2 git bundle\n':
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002353 return True
2354 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002355 if not quiet:
2356 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002357 return False
2358 except OSError:
2359 return False
2360
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002361 def _Checkout(self, rev, quiet=False):
2362 cmd = ['checkout']
2363 if quiet:
2364 cmd.append('-q')
2365 cmd.append(rev)
2366 cmd.append('--')
2367 if GitCommand(self, cmd).Wait() != 0:
2368 if self._allrefs:
2369 raise GitError('%s checkout %s ' % (self.name, rev))
2370
Anthony King7bdac712014-07-16 12:56:40 +01002371 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002372 cmd = ['cherry-pick']
2373 cmd.append(rev)
2374 cmd.append('--')
2375 if GitCommand(self, cmd).Wait() != 0:
2376 if self._allrefs:
2377 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2378
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302379 def _LsRemote(self, refs):
2380 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302381 p = GitCommand(self, cmd, capture_stdout=True)
2382 if p.Wait() == 0:
2383 if hasattr(p.stdout, 'decode'):
2384 return p.stdout.decode('utf-8')
2385 else:
2386 return p.stdout
2387 return None
2388
Anthony King7bdac712014-07-16 12:56:40 +01002389 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002390 cmd = ['revert']
2391 cmd.append('--no-edit')
2392 cmd.append(rev)
2393 cmd.append('--')
2394 if GitCommand(self, cmd).Wait() != 0:
2395 if self._allrefs:
2396 raise GitError('%s revert %s ' % (self.name, rev))
2397
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002398 def _ResetHard(self, rev, quiet=True):
2399 cmd = ['reset', '--hard']
2400 if quiet:
2401 cmd.append('-q')
2402 cmd.append(rev)
2403 if GitCommand(self, cmd).Wait() != 0:
2404 raise GitError('%s reset --hard %s ' % (self.name, rev))
2405
Martin Kellye4e94d22017-03-21 16:05:12 -07002406 def _SyncSubmodules(self, quiet=True):
2407 cmd = ['submodule', 'update', '--init', '--recursive']
2408 if quiet:
2409 cmd.append('-q')
2410 if GitCommand(self, cmd).Wait() != 0:
2411 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2412
Anthony King7bdac712014-07-16 12:56:40 +01002413 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002414 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002415 if onto is not None:
2416 cmd.extend(['--onto', onto])
2417 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002418 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002419 raise GitError('%s rebase %s ' % (self.name, upstream))
2420
Pierre Tardy3d125942012-05-04 12:18:12 +02002421 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002422 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002423 if ffonly:
2424 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002425 if GitCommand(self, cmd).Wait() != 0:
2426 raise GitError('%s merge %s ' % (self.name, head))
2427
Kevin Degiabaa7f32014-11-12 11:27:45 -07002428 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002429 init_git_dir = not os.path.exists(self.gitdir)
2430 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002431 try:
2432 # Initialize the bare repository, which contains all of the objects.
2433 if init_obj_dir:
2434 os.makedirs(self.objdir)
2435 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002436
Kevin Degib1a07b82015-07-27 13:33:43 -06002437 # If we have a separate directory to hold refs, initialize it as well.
2438 if self.objdir != self.gitdir:
2439 if init_git_dir:
2440 os.makedirs(self.gitdir)
2441
2442 if init_obj_dir or init_git_dir:
2443 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2444 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002445 try:
2446 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2447 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002448 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002449 print("Retrying clone after deleting %s" %
2450 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002451 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002452 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2453 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002454 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002455 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002456 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2457 except:
2458 raise e
2459 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002460
Kevin Degi384b3c52014-10-16 16:02:58 -06002461 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002462 mp = self.manifest.manifestProject
2463 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002464
Kevin Degib1a07b82015-07-27 13:33:43 -06002465 if ref_dir or mirror_git:
2466 if not mirror_git:
2467 mirror_git = os.path.join(ref_dir, self.name + '.git')
2468 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2469 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002470
Kevin Degib1a07b82015-07-27 13:33:43 -06002471 if os.path.exists(mirror_git):
2472 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002473
Kevin Degib1a07b82015-07-27 13:33:43 -06002474 elif os.path.exists(repo_git):
2475 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002476
Kevin Degib1a07b82015-07-27 13:33:43 -06002477 else:
2478 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002479
Kevin Degib1a07b82015-07-27 13:33:43 -06002480 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002481 if not os.path.isabs(ref_dir):
2482 # The alternate directory is relative to the object database.
2483 ref_dir = os.path.relpath(ref_dir,
2484 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002485 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2486 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002487
Kevin Degib1a07b82015-07-27 13:33:43 -06002488 self._UpdateHooks()
2489
2490 m = self.manifest.manifestProject.config
2491 for key in ['user.name', 'user.email']:
2492 if m.Has(key, include_defaults=False):
2493 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002494 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Francois Ferrandc5b0e232019-05-24 09:35:20 +02002495 self.config.SetString('filter.lfs.process', 'git-lfs filter-process --skip')
Kevin Degib1a07b82015-07-27 13:33:43 -06002496 if self.manifest.IsMirror:
2497 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002498 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002499 self.config.SetString('core.bare', None)
2500 except Exception:
2501 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002502 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002504 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002505 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002506
Jimmie Westera0444582012-10-24 13:44:42 +02002507 def _UpdateHooks(self):
2508 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002509 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002510
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002511 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002512 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002513 if not os.path.exists(hooks):
2514 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002515 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002516 name = os.path.basename(stock_hook)
2517
Victor Boivie65e0f352011-04-18 11:23:29 +02002518 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002519 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002520 # Don't install a Gerrit Code Review hook if this
2521 # project does not appear to use it for reviews.
2522 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002523 # Since the manifest project is one of those, but also
2524 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002525 continue
2526
2527 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002528 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002529 continue
2530 if os.path.exists(dst):
2531 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002532 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002533 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002534 _warn("%s: Not replacing locally modified %s hook",
2535 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002536 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002537 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002538 platform_utils.symlink(
2539 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002540 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002541 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002542 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002543 else:
2544 raise
2545
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002546 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002547 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002548 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002549 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002550 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002551 remote.review = self.remote.review
2552 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002553
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002554 if self.worktree:
2555 remote.ResetFetch(mirror=False)
2556 else:
2557 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002558 remote.Save()
2559
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002560 def _InitMRef(self):
2561 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002562 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002563
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002564 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002565 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002566
2567 def _InitAnyMRef(self, ref):
2568 cur = self.bare_ref.symref(ref)
2569
2570 if self.revisionId:
2571 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2572 msg = 'manifest set to %s' % self.revisionId
2573 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002574 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002575 else:
2576 remote = self.GetRemote(self.remote.name)
2577 dst = remote.ToLocal(self.revisionExpr)
2578 if cur != dst:
2579 msg = 'manifest set to %s' % self.revisionExpr
2580 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002581
Kevin Degi384b3c52014-10-16 16:02:58 -06002582 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002583 symlink_files = self.shareable_files[:]
2584 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002585 if share_refs:
2586 symlink_files += self.working_tree_files
2587 symlink_dirs += self.working_tree_dirs
2588 to_symlink = symlink_files + symlink_dirs
2589 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002590 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002591 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002592 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002593 # Fail if the links are pointing to the wrong place
2594 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002595 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002596 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002597 'work tree. If you\'re comfortable with the '
2598 'possibility of losing the work tree\'s git metadata,'
2599 ' use `repo sync --force-sync {0}` to '
2600 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002601
David James8d201162013-10-11 17:03:19 -07002602 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2603 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2604
2605 Args:
2606 gitdir: The bare git repository. Must already be initialized.
2607 dotgit: The repository you would like to initialize.
2608 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2609 Only one work tree can store refs under a given |gitdir|.
2610 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2611 This saves you the effort of initializing |dotgit| yourself.
2612 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002613 symlink_files = self.shareable_files[:]
2614 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002615 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002616 symlink_files += self.working_tree_files
2617 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002618 to_symlink = symlink_files + symlink_dirs
2619
2620 to_copy = []
2621 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002622 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002623
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002624 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002625 for name in set(to_copy).union(to_symlink):
2626 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002627 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002628 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002629
Kevin Degi384b3c52014-10-16 16:02:58 -06002630 if os.path.lexists(dst):
2631 continue
David James8d201162013-10-11 17:03:19 -07002632
2633 # If the source dir doesn't exist, create an empty dir.
2634 if name in symlink_dirs and not os.path.lexists(src):
2635 os.makedirs(src)
2636
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002637 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002638 platform_utils.symlink(
2639 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002640 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002641 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002642 shutil.copytree(src, dst)
2643 elif os.path.isfile(src):
2644 shutil.copy(src, dst)
2645
Conley Owens80b87fe2014-05-09 17:13:44 -07002646 # If the source file doesn't exist, ensure the destination
2647 # file doesn't either.
2648 if name in symlink_files and not os.path.lexists(src):
2649 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002650 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002651 except OSError:
2652 pass
2653
David James8d201162013-10-11 17:03:19 -07002654 except OSError as e:
2655 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002656 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002657 else:
2658 raise
2659
Martin Kellye4e94d22017-03-21 16:05:12 -07002660 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002661 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002662 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002663 try:
2664 if init_dotgit:
2665 os.makedirs(dotgit)
2666 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2667 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002668
Kevin Degiabaa7f32014-11-12 11:27:45 -07002669 try:
2670 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2671 except GitError as e:
2672 if force_sync:
2673 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002674 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002675 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002676 except:
2677 raise e
2678 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002679
Kevin Degib1a07b82015-07-27 13:33:43 -06002680 if init_dotgit:
2681 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002682
Kevin Degib1a07b82015-07-27 13:33:43 -06002683 cmd = ['read-tree', '--reset', '-u']
2684 cmd.append('-v')
2685 cmd.append(HEAD)
2686 if GitCommand(self, cmd).Wait() != 0:
Mikhail Naganovb5548382019-05-09 12:59:49 -07002687 raise GitError("cannot initialize work tree for " + self.name)
Victor Boivie0960b5b2010-11-26 13:42:13 +01002688
Martin Kellye4e94d22017-03-21 16:05:12 -07002689 if submodules:
2690 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002691 self._CopyAndLinkFiles()
2692 except Exception:
2693 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002694 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002695 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002696
Renaud Paquay788e9622017-01-27 11:41:12 -08002697 def _get_symlink_error_message(self):
2698 if platform_utils.isWindows():
2699 return ('Unable to create symbolic link. Please re-run the command as '
2700 'Administrator, or see '
2701 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2702 'for other options.')
2703 return 'filesystem must support symlinks'
2704
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002705 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002706 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002707
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002708 def _revlist(self, *args, **kw):
2709 a = []
2710 a.extend(args)
2711 a.append('--')
2712 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002713
2714 @property
2715 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002716 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002717
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002718 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002719 """Get logs between two revisions of this project."""
2720 comp = '..'
2721 if rev1:
2722 revs = [rev1]
2723 if rev2:
2724 revs.extend([comp, rev2])
2725 cmd = ['log', ''.join(revs)]
2726 out = DiffColoring(self.config)
2727 if out.is_on and color:
2728 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002729 if pretty_format is not None:
2730 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002731 if oneline:
2732 cmd.append('--oneline')
2733
2734 try:
2735 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2736 if log.Wait() == 0:
2737 return log.stdout
2738 except GitError:
2739 # worktree may not exist if groups changed for example. In that case,
2740 # try in gitdir instead.
2741 if not os.path.exists(self.worktree):
2742 return self.bare_git.log(*cmd[1:])
2743 else:
2744 raise
2745 return None
2746
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002747 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2748 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002749 """Get the list of logs from this revision to given revisionId"""
2750 logs = {}
2751 selfId = self.GetRevisionId(self._allrefs)
2752 toId = toProject.GetRevisionId(toProject._allrefs)
2753
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002754 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2755 pretty_format=pretty_format)
2756 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2757 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002758 return logs
2759
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002760 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002761
David James8d201162013-10-11 17:03:19 -07002762 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002763 self._project = project
2764 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002765 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002766
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002767 def LsOthers(self):
2768 p = GitCommand(self._project,
2769 ['ls-files',
2770 '-z',
2771 '--others',
2772 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002773 bare=False,
David James8d201162013-10-11 17:03:19 -07002774 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002775 capture_stdout=True,
2776 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002777 if p.Wait() == 0:
2778 out = p.stdout
2779 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002780 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002781 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002782 return []
2783
2784 def DiffZ(self, name, *args):
2785 cmd = [name]
2786 cmd.append('-z')
Eli Ribble7b4f0192019-05-02 18:21:42 -07002787 cmd.append('--ignore-submodules')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002788 cmd.extend(args)
2789 p = GitCommand(self._project,
2790 cmd,
David James8d201162013-10-11 17:03:19 -07002791 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002792 bare=False,
2793 capture_stdout=True,
2794 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002795 try:
2796 out = p.process.stdout.read()
2797 r = {}
2798 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002799 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002800 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002801 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002802 info = next(out)
2803 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002804 except StopIteration:
2805 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002806
2807 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002808
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002809 def __init__(self, path, omode, nmode, oid, nid, state):
2810 self.path = path
2811 self.src_path = None
2812 self.old_mode = omode
2813 self.new_mode = nmode
2814 self.old_id = oid
2815 self.new_id = nid
2816
2817 if len(state) == 1:
2818 self.status = state
2819 self.level = None
2820 else:
2821 self.status = state[:1]
2822 self.level = state[1:]
2823 while self.level.startswith('0'):
2824 self.level = self.level[1:]
2825
2826 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002827 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828 if info.status in ('R', 'C'):
2829 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002830 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002831 r[info.path] = info
2832 return r
2833 finally:
2834 p.Wait()
2835
2836 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002837 if self._bare:
2838 path = os.path.join(self._project.gitdir, HEAD)
2839 else:
2840 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002841 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002842 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002843 except IOError as e:
2844 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002845 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002846 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002847 finally:
2848 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302849 try:
2850 line = line.decode()
2851 except AttributeError:
2852 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002853 if line.startswith('ref: '):
2854 return line[5:-1]
2855 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002856
2857 def SetHead(self, ref, message=None):
2858 cmdv = []
2859 if message is not None:
2860 cmdv.extend(['-m', message])
2861 cmdv.append(HEAD)
2862 cmdv.append(ref)
2863 self.symbolic_ref(*cmdv)
2864
2865 def DetachHead(self, new, message=None):
2866 cmdv = ['--no-deref']
2867 if message is not None:
2868 cmdv.extend(['-m', message])
2869 cmdv.append(HEAD)
2870 cmdv.append(new)
2871 self.update_ref(*cmdv)
2872
2873 def UpdateRef(self, name, new, old=None,
2874 message=None,
2875 detach=False):
2876 cmdv = []
2877 if message is not None:
2878 cmdv.extend(['-m', message])
2879 if detach:
2880 cmdv.append('--no-deref')
2881 cmdv.append(name)
2882 cmdv.append(new)
2883 if old is not None:
2884 cmdv.append(old)
2885 self.update_ref(*cmdv)
2886
2887 def DeleteRef(self, name, old=None):
2888 if not old:
2889 old = self.rev_parse(name)
2890 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002891 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002892
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002893 def rev_list(self, *args, **kw):
2894 if 'format' in kw:
2895 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2896 else:
2897 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002898 cmdv.extend(args)
2899 p = GitCommand(self._project,
2900 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002901 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002902 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002903 capture_stdout=True,
2904 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002905 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002906 raise GitError('%s rev-list %s: %s' %
2907 (self._project.name, str(args), p.stderr))
Mike Frysinger81f5c592019-07-04 18:13:31 -04002908 return p.stdout.splitlines()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002909
2910 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002911 """Allow arbitrary git commands using pythonic syntax.
2912
2913 This allows you to do things like:
2914 git_obj.rev_parse('HEAD')
2915
2916 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2917 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002918 Any other positional arguments will be passed to the git command, and the
2919 following keyword arguments are supported:
2920 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002921
2922 Args:
2923 name: The name of the git command to call. Any '_' characters will
2924 be replaced with '-'.
2925
2926 Returns:
2927 A callable object that will try to call git with the named command.
2928 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002929 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002930
Dave Borowitz091f8932012-10-23 17:01:04 -07002931 def runner(*args, **kwargs):
2932 cmdv = []
2933 config = kwargs.pop('config', None)
2934 for k in kwargs:
2935 raise TypeError('%s() got an unexpected keyword argument %r'
2936 % (name, k))
2937 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002938 if not git_require((1, 7, 2)):
2939 raise ValueError('cannot set config on command line for %s()'
2940 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302941 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002942 cmdv.append('-c')
2943 cmdv.append('%s=%s' % (k, v))
2944 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002945 cmdv.extend(args)
2946 p = GitCommand(self._project,
2947 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002948 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002949 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002950 capture_stdout=True,
2951 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002952 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002953 raise GitError('%s %s: %s' %
2954 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002955 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302956 try:
Conley Owensedd01512013-09-26 12:59:58 -07002957 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302958 except AttributeError:
2959 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002960 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2961 return r[:-1]
2962 return r
2963 return runner
2964
2965
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002966class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002967
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002968 def __str__(self):
2969 return 'prior sync failed; rebase still in progress'
2970
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002971
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002972class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002973
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002974 def __str__(self):
2975 return 'contains uncommitted changes'
2976
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002977
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002978class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002979
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002980 def __init__(self, project, text):
2981 self.project = project
2982 self.text = text
2983
2984 def Print(self, syncbuf):
2985 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2986 syncbuf.out.nl()
2987
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002988
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002989class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002990
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002991 def __init__(self, project, why):
2992 self.project = project
2993 self.why = why
2994
2995 def Print(self, syncbuf):
2996 syncbuf.out.fail('error: %s/: %s',
2997 self.project.relpath,
2998 str(self.why))
2999 syncbuf.out.nl()
3000
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003001
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003002class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003003
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003004 def __init__(self, project, action):
3005 self.project = project
3006 self.action = action
3007
3008 def Run(self, syncbuf):
3009 out = syncbuf.out
3010 out.project('project %s/', self.project.relpath)
3011 out.nl()
3012 try:
3013 self.action()
3014 out.nl()
3015 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09003016 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003017 out.nl()
3018 return False
3019
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003020
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003021class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003022
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003023 def __init__(self, config):
3024 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01003025 self.project = self.printer('header', attr='bold')
3026 self.info = self.printer('info')
3027 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003028
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003029
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003030class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003031
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003032 def __init__(self, config, detach_head=False):
3033 self._messages = []
3034 self._failures = []
3035 self._later_queue1 = []
3036 self._later_queue2 = []
3037
3038 self.out = _SyncColoring(config)
3039 self.out.redirect(sys.stderr)
3040
3041 self.detach_head = detach_head
3042 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07003043 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003044
3045 def info(self, project, fmt, *args):
3046 self._messages.append(_InfoMessage(project, fmt % args))
3047
3048 def fail(self, project, err=None):
3049 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07003050 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003051
3052 def later1(self, project, what):
3053 self._later_queue1.append(_Later(project, what))
3054
3055 def later2(self, project, what):
3056 self._later_queue2.append(_Later(project, what))
3057
3058 def Finish(self):
3059 self._PrintMessages()
3060 self._RunLater()
3061 self._PrintMessages()
3062 return self.clean
3063
David Rileye0684ad2017-04-05 00:02:59 -07003064 def Recently(self):
3065 recent_clean = self.recent_clean
3066 self.recent_clean = True
3067 return recent_clean
3068
3069 def _MarkUnclean(self):
3070 self.clean = False
3071 self.recent_clean = False
3072
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003073 def _RunLater(self):
3074 for q in ['_later_queue1', '_later_queue2']:
3075 if not self._RunQueue(q):
3076 return
3077
3078 def _RunQueue(self, queue):
3079 for m in getattr(self, queue):
3080 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003081 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003082 return False
3083 setattr(self, queue, [])
3084 return True
3085
3086 def _PrintMessages(self):
3087 for m in self._messages:
3088 m.Print(self)
3089 for m in self._failures:
3090 m.Print(self)
3091
3092 self._messages = []
3093 self._failures = []
3094
3095
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003096class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003097
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003098 """A special project housed under .repo.
3099 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003100
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003101 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003102 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003103 manifest=manifest,
3104 name=name,
3105 gitdir=gitdir,
3106 objdir=gitdir,
3107 worktree=worktree,
3108 remote=RemoteSpec('origin'),
3109 relpath='.repo/%s' % name,
3110 revisionExpr='refs/heads/master',
3111 revisionId=None,
3112 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003113
3114 def PreSync(self):
3115 if self.Exists:
3116 cb = self.CurrentBranch
3117 if cb:
3118 base = self.GetBranch(cb).merge
3119 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003120 self.revisionExpr = base
3121 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003122
Martin Kelly224a31a2017-07-10 14:46:25 -07003123 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003124 """ Prepare MetaProject for manifest branch switch
3125 """
3126
3127 # detach and delete manifest branch, allowing a new
3128 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003129 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003130 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003131 syncbuf.Finish()
3132
3133 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003134 ['update-ref', '-d', 'refs/heads/default'],
3135 capture_stdout=True,
3136 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003137
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003138 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003139 def LastFetch(self):
3140 try:
3141 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3142 return os.path.getmtime(fh)
3143 except OSError:
3144 return 0
3145
3146 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003147 def HasChanges(self):
3148 """Has the remote received new commits not yet checked out?
3149 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003150 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003151 return False
3152
David Pursehouse8a68ff92012-09-24 12:15:13 +09003153 all_refs = self.bare_ref.all
3154 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003155 head = self.work_git.GetHead()
3156 if head.startswith(R_HEADS):
3157 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003158 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003159 except KeyError:
3160 head = None
3161
3162 if revid == head:
3163 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003164 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003165 return True
3166 return False