blob: 5297a5cb3010fbebb78f3d08ed4a45c5bb1fca3c [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080036from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080037from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070038import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070039from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Shawn O. Pearced237b692009-04-17 18:49:50 -070041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
David Pursehouse59bbb582013-05-17 10:49:33 +090043from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040044if is_python3():
45 import urllib.parse
46else:
47 import imp
48 import urlparse
49 urllib = imp.new_module('urllib')
50 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090051 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090053 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070066 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080068 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700106 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700107 d = os.path.join(d, 'hooks')
108 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000179 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200180 private=False,
181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
190 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200191 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800192 validate_certs=validate_certs,
193 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700194
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700195 def GetPublishedRefs(self):
196 refs = {}
197 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700198 self.branch.remote.SshReviewUrl(self.project.UserEmail),
199 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700200 for line in output.split('\n'):
201 try:
202 (sha, ref) = line.split()
203 refs[sha] = ref
204 except ValueError:
205 pass
206
207 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700209
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700211
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212 def __init__(self, config):
213 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100214 self.project = self.printer('header', attr='bold')
215 self.branch = self.printer('header', attr='bold')
216 self.nobranch = self.printer('nobranch', fg='red')
217 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
Anthony King7bdac712014-07-16 12:56:40 +0100219 self.added = self.printer('added', fg='green')
220 self.changed = self.printer('changed', fg='red')
221 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
223
224class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700225
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226 def __init__(self, config):
227 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100228 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700230
Anthony King7bdac712014-07-16 12:56:40 +0100231class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700232
James W. Mills24c13082012-04-12 15:04:13 -0500233 def __init__(self, name, value, keep):
234 self.name = name
235 self.value = value
236 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700238
Anthony King7bdac712014-07-16 12:56:40 +0100239class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700240
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800241 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242 self.src = src
243 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 self.abs_src = abssrc
245 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246
247 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800248 src = self.abs_src
249 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 # copy file if it does not exist or is out of date
251 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
252 try:
253 # remove existing file first, since it might be read-only
254 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800255 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400256 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200257 dest_dir = os.path.dirname(dest)
258 if not os.path.isdir(dest_dir):
259 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260 shutil.copy(src, dest)
261 # make the file read-only
262 mode = os.stat(dest)[stat.ST_MODE]
263 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
264 os.chmod(dest, mode)
265 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700266 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700268
Anthony King7bdac712014-07-16 12:56:40 +0100269class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700270
Wink Saville4c426ef2015-06-03 08:05:17 -0700271 def __init__(self, git_worktree, src, dest, relsrc, absdest):
272 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500273 self.src = src
274 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700275 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.abs_dest = absdest
277
Wink Saville4c426ef2015-06-03 08:05:17 -0700278 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700280 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 try:
282 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800283 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800284 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700286 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500287 if not os.path.isdir(dest_dir):
288 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700289 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700291 _error('Cannot link file %s to %s', relSrc, absDest)
292
293 def _Link(self):
294 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
295 on the src linking all of the files in the source in to the destination
296 directory.
297 """
298 # We use the absSrc to handle the situation where the current directory
299 # is not the root of the repo
300 absSrc = os.path.join(self.git_worktree, self.src)
301 if os.path.exists(absSrc):
302 # Entity exists so just a simple one to one link operation
303 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
304 else:
305 # Entity doesn't exist assume there is a wild card
306 absDestDir = self.abs_dest
307 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
308 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700309 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700310 else:
311 absSrcFiles = glob.glob(absSrc)
312 for absSrcFile in absSrcFiles:
313 # Create a releative path from source dir to destination dir
314 absSrcDir = os.path.dirname(absSrcFile)
315 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
316
317 # Get the source file name
318 srcFile = os.path.basename(absSrcFile)
319
320 # Now form the final full paths to srcFile. They will be
321 # absolute for the desintaiton and relative for the srouce.
322 absDest = os.path.join(absDestDir, srcFile)
323 relSrc = os.path.join(relSrcDir, srcFile)
324 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500325
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700326
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700328
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700329 def __init__(self,
330 name,
Anthony King7bdac712014-07-16 12:56:40 +0100331 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700332 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100333 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700334 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700335 orig_name=None,
336 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700337 self.name = name
338 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700339 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100341 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700342 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700343 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700344
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700345
Doug Anderson37282b42011-03-04 11:54:18 -0800346class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700347
Doug Anderson37282b42011-03-04 11:54:18 -0800348 """A RepoHook contains information about a script to run as a hook.
349
350 Hooks are used to run a python script before running an upload (for instance,
351 to run presubmit checks). Eventually, we may have hooks for other actions.
352
353 This shouldn't be confused with files in the 'repo/hooks' directory. Those
354 files are copied into each '.git/hooks' folder for each project. Repo-level
355 hooks are associated instead with repo actions.
356
357 Hooks are always python. When a hook is run, we will load the hook into the
358 interpreter and execute its main() function.
359 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700360
Doug Anderson37282b42011-03-04 11:54:18 -0800361 def __init__(self,
362 hook_type,
363 hooks_project,
364 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400365 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800366 abort_if_user_denies=False):
367 """RepoHook constructor.
368
369 Params:
370 hook_type: A string representing the type of hook. This is also used
371 to figure out the name of the file containing the hook. For
372 example: 'pre-upload'.
373 hooks_project: The project containing the repo hooks. If you have a
374 manifest, this is manifest.repo_hooks_project. OK if this is None,
375 which will make the hook a no-op.
376 topdir: Repo's top directory (the one containing the .repo directory).
377 Scripts will run with CWD as this directory. If you have a manifest,
378 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400379 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800380 abort_if_user_denies: If True, we'll throw a HookError() if the user
381 doesn't allow us to run the hook.
382 """
383 self._hook_type = hook_type
384 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400385 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800386 self._topdir = topdir
387 self._abort_if_user_denies = abort_if_user_denies
388
389 # Store the full path to the script for convenience.
390 if self._hooks_project:
391 self._script_fullpath = os.path.join(self._hooks_project.worktree,
392 self._hook_type + '.py')
393 else:
394 self._script_fullpath = None
395
396 def _GetHash(self):
397 """Return a hash of the contents of the hooks directory.
398
399 We'll just use git to do this. This hash has the property that if anything
400 changes in the directory we will return a different has.
401
402 SECURITY CONSIDERATION:
403 This hash only represents the contents of files in the hook directory, not
404 any other files imported or called by hooks. Changes to imported files
405 can change the script behavior without affecting the hash.
406
407 Returns:
408 A string representing the hash. This will always be ASCII so that it can
409 be printed to the user easily.
410 """
411 assert self._hooks_project, "Must have hooks to calculate their hash."
412
413 # We will use the work_git object rather than just calling GetRevisionId().
414 # That gives us a hash of the latest checked in version of the files that
415 # the user will actually be executing. Specifically, GetRevisionId()
416 # doesn't appear to change even if a user checks out a different version
417 # of the hooks repo (via git checkout) nor if a user commits their own revs.
418 #
419 # NOTE: Local (non-committed) changes will not be factored into this hash.
420 # I think this is OK, since we're really only worried about warning the user
421 # about upstream changes.
422 return self._hooks_project.work_git.rev_parse('HEAD')
423
424 def _GetMustVerb(self):
425 """Return 'must' if the hook is required; 'should' if not."""
426 if self._abort_if_user_denies:
427 return 'must'
428 else:
429 return 'should'
430
431 def _CheckForHookApproval(self):
432 """Check to see whether this hook has been approved.
433
Mike Frysinger40252c22016-08-15 21:23:44 -0400434 We'll accept approval of manifest URLs if they're using secure transports.
435 This way the user can say they trust the manifest hoster. For insecure
436 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800437
438 Note that we ask permission for each individual hook even though we use
439 the hash of all hooks when detecting changes. We'd like the user to be
440 able to approve / deny each hook individually. We only use the hash of all
441 hooks because there is no other easy way to detect changes to local imports.
442
443 Returns:
444 True if this hook is approved to run; False otherwise.
445
446 Raises:
447 HookError: Raised if the user doesn't approve and abort_if_user_denies
448 was passed to the consturctor.
449 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400450 if self._ManifestUrlHasSecureScheme():
451 return self._CheckForHookApprovalManifest()
452 else:
453 return self._CheckForHookApprovalHash()
454
455 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
456 changed_prompt):
457 """Check for approval for a particular attribute and hook.
458
459 Args:
460 subkey: The git config key under [repo.hooks.<hook_type>] to store the
461 last approved string.
462 new_val: The new value to compare against the last approved one.
463 main_prompt: Message to display to the user to ask for approval.
464 changed_prompt: Message explaining why we're re-asking for approval.
465
466 Returns:
467 True if this hook is approved to run; False otherwise.
468
469 Raises:
470 HookError: Raised if the user doesn't approve and abort_if_user_denies
471 was passed to the consturctor.
472 """
Doug Anderson37282b42011-03-04 11:54:18 -0800473 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400474 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800475
Mike Frysinger40252c22016-08-15 21:23:44 -0400476 # Get the last value that the user approved for this hook; may be None.
477 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800480 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400481 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800482 # Approval matched. We're done.
483 return True
484 else:
485 # Give the user a reason why we're prompting, since they last told
486 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400487 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800488 else:
489 prompt = ''
490
491 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
492 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400493 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530494 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900495 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800496
497 # User is doing a one-time approval.
498 if response in ('y', 'yes'):
499 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400500 elif response == 'always':
501 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800502 return True
503
504 # For anything else, we'll assume no approval.
505 if self._abort_if_user_denies:
506 raise HookError('You must allow the %s hook or use --no-verify.' %
507 self._hook_type)
508
509 return False
510
Mike Frysinger40252c22016-08-15 21:23:44 -0400511 def _ManifestUrlHasSecureScheme(self):
512 """Check if the URI for the manifest is a secure transport."""
513 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
514 parse_results = urllib.parse.urlparse(self._manifest_url)
515 return parse_results.scheme in secure_schemes
516
517 def _CheckForHookApprovalManifest(self):
518 """Check whether the user has approved this manifest host.
519
520 Returns:
521 True if this hook is approved to run; False otherwise.
522 """
523 return self._CheckForHookApprovalHelper(
524 'approvedmanifest',
525 self._manifest_url,
526 'Run hook scripts from %s' % (self._manifest_url,),
527 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
528
529 def _CheckForHookApprovalHash(self):
530 """Check whether the user has approved the hooks repo.
531
532 Returns:
533 True if this hook is approved to run; False otherwise.
534 """
535 prompt = ('Repo %s run the script:\n'
536 ' %s\n'
537 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700538 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400539 return self._CheckForHookApprovalHelper(
540 'approvedhash',
541 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700542 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400543 'Scripts have changed since %s was allowed.' % (self._hook_type,))
544
Doug Anderson37282b42011-03-04 11:54:18 -0800545 def _ExecuteHook(self, **kwargs):
546 """Actually execute the given hook.
547
548 This will run the hook's 'main' function in our python interpreter.
549
550 Args:
551 kwargs: Keyword arguments to pass to the hook. These are often specific
552 to the hook type. For instance, pre-upload hooks will contain
553 a project_list.
554 """
555 # Keep sys.path and CWD stashed away so that we can always restore them
556 # upon function exit.
557 orig_path = os.getcwd()
558 orig_syspath = sys.path
559
560 try:
561 # Always run hooks with CWD as topdir.
562 os.chdir(self._topdir)
563
564 # Put the hook dir as the first item of sys.path so hooks can do
565 # relative imports. We want to replace the repo dir as [0] so
566 # hooks can't import repo files.
567 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
568
569 # Exec, storing global context in the context dict. We catch exceptions
570 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500571 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800572 try:
Anthony King70f68902014-05-05 21:15:34 +0100573 exec(compile(open(self._script_fullpath).read(),
574 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800575 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700576 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
577 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800578
579 # Running the script should have defined a main() function.
580 if 'main' not in context:
581 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
582
Doug Anderson37282b42011-03-04 11:54:18 -0800583 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
584 # We don't actually want hooks to define their main with this argument--
585 # it's there to remind them that their hook should always take **kwargs.
586 # For instance, a pre-upload hook should be defined like:
587 # def main(project_list, **kwargs):
588 #
589 # This allows us to later expand the API without breaking old hooks.
590 kwargs = kwargs.copy()
591 kwargs['hook_should_take_kwargs'] = True
592
593 # Call the main function in the hook. If the hook should cause the
594 # build to fail, it will raise an Exception. We'll catch that convert
595 # to a HookError w/ just the failing traceback.
596 try:
597 context['main'](**kwargs)
598 except Exception:
599 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700600 'above.' % (traceback.format_exc(),
601 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800602 finally:
603 # Restore sys.path and CWD.
604 sys.path = orig_syspath
605 os.chdir(orig_path)
606
607 def Run(self, user_allows_all_hooks, **kwargs):
608 """Run the hook.
609
610 If the hook doesn't exist (because there is no hooks project or because
611 this particular hook is not enabled), this is a no-op.
612
613 Args:
614 user_allows_all_hooks: If True, we will never prompt about running the
615 hook--we'll just assume it's OK to run it.
616 kwargs: Keyword arguments to pass to the hook. These are often specific
617 to the hook type. For instance, pre-upload hooks will contain
618 a project_list.
619
620 Raises:
621 HookError: If there was a problem finding the hook or the user declined
622 to run a required hook (from _CheckForHookApproval).
623 """
624 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700625 if ((not self._hooks_project) or (self._hook_type not in
626 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800627 return
628
629 # Bail with a nice error if we can't find the hook.
630 if not os.path.isfile(self._script_fullpath):
631 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
632
633 # Make sure the user is OK with running the hook.
634 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
635 return
636
637 # Run the hook with the same version of python we're using.
638 self._ExecuteHook(**kwargs)
639
640
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700641class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600642 # These objects can be shared between several working trees.
643 shareable_files = ['description', 'info']
644 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
645 # These objects can only be used by a single working tree.
646 working_tree_files = ['config', 'packed-refs', 'shallow']
647 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700648
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700649 def __init__(self,
650 manifest,
651 name,
652 remote,
653 gitdir,
David James8d201162013-10-11 17:03:19 -0700654 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700655 worktree,
656 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700657 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800658 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100659 rebase=True,
660 groups=None,
661 sync_c=False,
662 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900663 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100664 clone_depth=None,
665 upstream=None,
666 parent=None,
667 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900668 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700669 optimized_fetch=False,
670 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800671 """Init a Project object.
672
673 Args:
674 manifest: The XmlManifest object.
675 name: The `name` attribute of manifest.xml's project element.
676 remote: RemoteSpec object specifying its remote's properties.
677 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700678 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800679 worktree: Absolute path of git working tree.
680 relpath: Relative path of git working tree to repo's top directory.
681 revisionExpr: The `revision` attribute of manifest.xml's project element.
682 revisionId: git commit id for checking out.
683 rebase: The `rebase` attribute of manifest.xml's project element.
684 groups: The `groups` attribute of manifest.xml's project element.
685 sync_c: The `sync-c` attribute of manifest.xml's project element.
686 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900687 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800688 upstream: The `upstream` attribute of manifest.xml's project element.
689 parent: The parent Project object.
690 is_derived: False if the project was explicitly defined in the manifest;
691 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400692 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900693 optimized_fetch: If True, when a project is set to a sha1 revision, only
694 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700695 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800696 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697 self.manifest = manifest
698 self.name = name
699 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800700 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700701 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800702 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700703 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800704 else:
705 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700706 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700707 self.revisionExpr = revisionExpr
708
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700709 if revisionId is None \
710 and revisionExpr \
711 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700712 self.revisionId = revisionExpr
713 else:
714 self.revisionId = revisionId
715
Mike Pontillod3153822012-02-28 11:53:24 -0800716 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700717 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700718 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 self.sync_s = sync_s
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900720 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900721 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700722 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800723 self.parent = parent
724 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900725 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800726 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800727
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700728 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700729 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500730 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500731 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700732 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
733 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800735 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700736 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800737 else:
738 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700739 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700740 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700741 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400742 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700743 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700744
Doug Anderson37282b42011-03-04 11:54:18 -0800745 # This will be filled in if a project is later identified to be the
746 # project containing repo hooks.
747 self.enabled_repo_hooks = []
748
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800750 def Derived(self):
751 return self.is_derived
752
753 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600755 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700756
757 @property
758 def CurrentBranch(self):
759 """Obtain the name of the currently checked out branch.
760 The branch name omits the 'refs/heads/' prefix.
761 None is returned if the project is on a detached HEAD.
762 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700763 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700764 if b.startswith(R_HEADS):
765 return b[len(R_HEADS):]
766 return None
767
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700768 def IsRebaseInProgress(self):
769 w = self.worktree
770 g = os.path.join(w, '.git')
771 return os.path.exists(os.path.join(g, 'rebase-apply')) \
772 or os.path.exists(os.path.join(g, 'rebase-merge')) \
773 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200774
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700775 def IsDirty(self, consider_untracked=True):
776 """Is the working directory modified in some way?
777 """
778 self.work_git.update_index('-q',
779 '--unmerged',
780 '--ignore-missing',
781 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900782 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700783 return True
784 if self.work_git.DiffZ('diff-files'):
785 return True
786 if consider_untracked and self.work_git.LsOthers():
787 return True
788 return False
789
790 _userident_name = None
791 _userident_email = None
792
793 @property
794 def UserName(self):
795 """Obtain the user's personal name.
796 """
797 if self._userident_name is None:
798 self._LoadUserIdentity()
799 return self._userident_name
800
801 @property
802 def UserEmail(self):
803 """Obtain the user's email address. This is very likely
804 to be their Gerrit login.
805 """
806 if self._userident_email is None:
807 self._LoadUserIdentity()
808 return self._userident_email
809
810 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900811 u = self.bare_git.var('GIT_COMMITTER_IDENT')
812 m = re.compile("^(.*) <([^>]*)> ").match(u)
813 if m:
814 self._userident_name = m.group(1)
815 self._userident_email = m.group(2)
816 else:
817 self._userident_name = ''
818 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700819
820 def GetRemote(self, name):
821 """Get the configuration for a single remote.
822 """
823 return self.config.GetRemote(name)
824
825 def GetBranch(self, name):
826 """Get the configuration for a single branch.
827 """
828 return self.config.GetBranch(name)
829
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700830 def GetBranches(self):
831 """Get all existing local branches.
832 """
833 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900834 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700835 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530837 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700838 if name.startswith(R_HEADS):
839 name = name[len(R_HEADS):]
840 b = self.GetBranch(name)
841 b.current = name == current
842 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900843 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700844 heads[name] = b
845
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530846 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700847 if name.startswith(R_PUB):
848 name = name[len(R_PUB):]
849 b = heads.get(name)
850 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900851 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700852
853 return heads
854
Colin Cross5acde752012-03-28 20:15:45 -0700855 def MatchesGroups(self, manifest_groups):
856 """Returns true if the manifest groups specified at init should cause
857 this project to be synced.
858 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700859 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700860
861 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700862 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700863 manifest_groups: "-group1,group2"
864 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500865
866 The special manifest group "default" will match any project that
867 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700868 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500869 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700870 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700871 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500872 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700873
Conley Owens971de8e2012-04-16 10:36:08 -0700874 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700875 for group in expanded_manifest_groups:
876 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700877 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700878 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700879 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700880
Conley Owens971de8e2012-04-16 10:36:08 -0700881 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700882
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700883# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700884 def UncommitedFiles(self, get_all=True):
885 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700886
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700887 Args:
888 get_all: a boolean, if True - get information about all different
889 uncommitted files. If False - return as soon as any kind of
890 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500891 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700892 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500893 self.work_git.update_index('-q',
894 '--unmerged',
895 '--ignore-missing',
896 '--refresh')
897 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700898 details.append("rebase in progress")
899 if not get_all:
900 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500901
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700902 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
903 if changes:
904 details.extend(changes)
905 if not get_all:
906 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500907
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700908 changes = self.work_git.DiffZ('diff-files').keys()
909 if changes:
910 details.extend(changes)
911 if not get_all:
912 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500913
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700914 changes = self.work_git.LsOthers()
915 if changes:
916 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500917
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700918 return details
919
920 def HasChanges(self):
921 """Returns true if there are uncommitted changes.
922 """
923 if self.UncommitedFiles(get_all=False):
924 return True
925 else:
926 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500927
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600928 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700929 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200930
931 Args:
932 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600933 quiet: If True then only print the project name. Do not print
934 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 """
936 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700937 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200938 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700939 print(file=output_redir)
940 print('project %s/' % self.relpath, file=output_redir)
941 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 return
943
944 self.work_git.update_index('-q',
945 '--unmerged',
946 '--ignore-missing',
947 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700948 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700949 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
950 df = self.work_git.DiffZ('diff-files')
951 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100952 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700953 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954
955 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700956 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200957 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700958 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700959
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600960 if quiet:
961 out.nl()
962 return 'DIRTY'
963
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700964 branch = self.CurrentBranch
965 if branch is None:
966 out.nobranch('(*** NO BRANCH ***)')
967 else:
968 out.branch('branch %s', branch)
969 out.nl()
970
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700971 if rb:
972 out.important('prior sync failed; rebase still in progress')
973 out.nl()
974
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975 paths = list()
976 paths.extend(di.keys())
977 paths.extend(df.keys())
978 paths.extend(do)
979
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530980 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900981 try:
982 i = di[p]
983 except KeyError:
984 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700985
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900986 try:
987 f = df[p]
988 except KeyError:
989 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200990
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900991 if i:
992 i_status = i.status.upper()
993 else:
994 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700995
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900996 if f:
997 f_status = f.status.lower()
998 else:
999 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001000
1001 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001002 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001003 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001004 else:
1005 line = ' %s%s\t%s' % (i_status, f_status, p)
1006
1007 if i and not f:
1008 out.added('%s', line)
1009 elif (i and f) or (not i and f):
1010 out.changed('%s', line)
1011 elif not i and not f:
1012 out.untracked('%s', line)
1013 else:
1014 out.write('%s', line)
1015 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001016
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001017 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018
pelyad67872d2012-03-28 14:49:58 +03001019 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 """Prints the status of the repository to stdout.
1021 """
1022 out = DiffColoring(self.config)
1023 cmd = ['diff']
1024 if out.is_on:
1025 cmd.append('--color')
1026 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001027 if absolute_paths:
1028 cmd.append('--src-prefix=a/%s/' % self.relpath)
1029 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001030 cmd.append('--')
1031 p = GitCommand(self,
1032 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001033 capture_stdout=True,
1034 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 has_diff = False
1036 for line in p.process.stdout:
1037 if not has_diff:
1038 out.nl()
1039 out.project('project %s/' % self.relpath)
1040 out.nl()
1041 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001042 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001043 p.Wait()
1044
1045
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001046# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047
David Pursehouse8a68ff92012-09-24 12:15:13 +09001048 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001049 """Was the branch published (uploaded) for code review?
1050 If so, returns the SHA-1 hash of the last published
1051 state for the branch.
1052 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001053 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 try:
1056 return self.bare_git.rev_parse(key)
1057 except GitError:
1058 return None
1059 else:
1060 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001061 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001062 except KeyError:
1063 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064
David Pursehouse8a68ff92012-09-24 12:15:13 +09001065 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 """Prunes any stale published refs.
1067 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001068 if all_refs is None:
1069 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070 heads = set()
1071 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301072 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001073 if name.startswith(R_HEADS):
1074 heads.add(name)
1075 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001076 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301078 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079 n = name[len(R_PUB):]
1080 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001081 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001083 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 """List any branches which can be uploaded for review.
1085 """
1086 heads = {}
1087 pubed = {}
1088
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001091 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001093 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
1095 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301096 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001097 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001099 if selected_branch and branch != selected_branch:
1100 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001101
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001102 rb = self.GetUploadableBranch(branch)
1103 if rb:
1104 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001105 return ready
1106
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001107 def GetUploadableBranch(self, branch_name):
1108 """Get a single uploadable branch, or None.
1109 """
1110 branch = self.GetBranch(branch_name)
1111 base = branch.LocalMerge
1112 if branch.LocalMerge:
1113 rb = ReviewableBranch(self, branch, base)
1114 if rb.commits:
1115 return rb
1116 return None
1117
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001118 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001119 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001120 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001121 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001122 private=False,
1123 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001124 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001125 validate_certs=True,
1126 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001127 """Uploads the named branch for code review.
1128 """
1129 if branch is None:
1130 branch = self.CurrentBranch
1131 if branch is None:
1132 raise GitError('not currently on a branch')
1133
1134 branch = self.GetBranch(branch)
1135 if not branch.LocalMerge:
1136 raise GitError('branch %s does not track a remote' % branch.name)
1137 if not branch.remote.review:
1138 raise GitError('remote %s has no review url' % branch.remote.name)
1139
Bryan Jacobsf609f912013-05-06 13:36:24 -04001140 if dest_branch is None:
1141 dest_branch = self.dest_branch
1142 if dest_branch is None:
1143 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001144 if not dest_branch.startswith(R_HEADS):
1145 dest_branch = R_HEADS + dest_branch
1146
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001147 if not branch.remote.projectname:
1148 branch.remote.projectname = self.name
1149 branch.remote.Save()
1150
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001151 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001152 if url is None:
1153 raise UploadError('review not configured')
1154 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001155
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001156 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001157 rp = ['gerrit receive-pack']
1158 for e in people[0]:
1159 rp.append('--reviewer=%s' % sq(e))
1160 for e in people[1]:
1161 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001162 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001163
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001164 for push_option in (push_options or []):
1165 cmd.append('-o')
1166 cmd.append(push_option)
1167
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001168 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001169
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001170 if dest_branch.startswith(R_HEADS):
1171 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001172
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001173 upload_type = 'for'
1174 if draft:
1175 upload_type = 'drafts'
1176
1177 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1178 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001179 if auto_topic:
1180 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001181
Shawn Pearce45d21682013-02-28 00:35:51 -08001182 if not url.startswith('ssh://'):
1183 rp = ['r=%s' % p for p in people[0]] + \
1184 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001185 if private:
1186 rp = rp + ['private']
1187 if wip:
1188 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001189 if rp:
1190 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001191 cmd.append(ref_spec)
1192
Anthony King7bdac712014-07-16 12:56:40 +01001193 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001194 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195
1196 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1197 self.bare_git.UpdateRef(R_PUB + branch.name,
1198 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001199 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200
1201
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001202# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203
Julien Campergue335f5ef2013-10-16 11:02:35 +02001204 def _ExtractArchive(self, tarpath, path=None):
1205 """Extract the given tar on its current location
1206
1207 Args:
1208 - tarpath: The path to the actual tar file
1209
1210 """
1211 try:
1212 with tarfile.open(tarpath, 'r') as tar:
1213 tar.extractall(path=path)
1214 return True
1215 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001216 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001217 return False
1218
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001219 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001220 quiet=False,
1221 is_new=None,
1222 current_branch_only=False,
1223 force_sync=False,
1224 clone_bundle=True,
1225 no_tags=False,
1226 archive=False,
1227 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001228 prune=False,
1229 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001230 """Perform only the network IO portion of the sync process.
1231 Local working directory/branch state is not affected.
1232 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001233 if archive and not isinstance(self, MetaProject):
1234 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001235 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001236 return False
1237
1238 name = self.relpath.replace('\\', '/')
1239 name = name.replace('/', '_')
1240 tarpath = '%s.tar' % name
1241 topdir = self.manifest.topdir
1242
1243 try:
1244 self._FetchArchive(tarpath, cwd=topdir)
1245 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001246 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001247 return False
1248
1249 # From now on, we only need absolute tarpath
1250 tarpath = os.path.join(topdir, tarpath)
1251
1252 if not self._ExtractArchive(tarpath, path=topdir):
1253 return False
1254 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001255 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001256 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001257 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001258 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001259 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001260 if is_new is None:
1261 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001262 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001263 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001264 else:
1265 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001266 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001267
1268 if is_new:
1269 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1270 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001271 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001272 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001273 # This works for both absolute and relative alternate directories.
1274 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001275 finally:
1276 fd.close()
1277 except IOError:
1278 alt_dir = None
1279 else:
1280 alt_dir = None
1281
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001282 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001283 and alt_dir is None \
1284 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001285 is_new = False
1286
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001287 if not current_branch_only:
1288 if self.sync_c:
1289 current_branch_only = True
1290 elif not self.manifest._loaded:
1291 # Manifest cannot check defaults until it syncs.
1292 current_branch_only = False
1293 elif self.manifest.default.sync_c:
1294 current_branch_only = True
1295
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001296 if not no_tags:
1297 if not self.sync_tags:
1298 no_tags = True
1299
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001300 if self.clone_depth:
1301 depth = self.clone_depth
1302 else:
1303 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1304
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001305 need_to_fetch = not (optimized_fetch and
1306 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001307 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001308 if (need_to_fetch and
1309 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1310 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001311 no_tags=no_tags, prune=prune, depth=depth,
1312 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001313 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001314
1315 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001316 self._InitMRef()
1317 else:
1318 self._InitMirrorHead()
1319 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001320 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001321 except OSError:
1322 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001323 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001324
1325 def PostRepoUpgrade(self):
1326 self._InitHooks()
1327
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001328 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001329 if self.manifest.isGitcClient:
1330 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001331 for copyfile in self.copyfiles:
1332 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001333 for linkfile in self.linkfiles:
1334 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001335
Julien Camperguedd654222014-01-09 16:21:37 +01001336 def GetCommitRevisionId(self):
1337 """Get revisionId of a commit.
1338
1339 Use this method instead of GetRevisionId to get the id of the commit rather
1340 than the id of the current git object (for example, a tag)
1341
1342 """
1343 if not self.revisionExpr.startswith(R_TAGS):
1344 return self.GetRevisionId(self._allrefs)
1345
1346 try:
1347 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1348 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001349 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1350 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001351
David Pursehouse8a68ff92012-09-24 12:15:13 +09001352 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001353 if self.revisionId:
1354 return self.revisionId
1355
1356 rem = self.GetRemote(self.remote.name)
1357 rev = rem.ToLocal(self.revisionExpr)
1358
David Pursehouse8a68ff92012-09-24 12:15:13 +09001359 if all_refs is not None and rev in all_refs:
1360 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001361
1362 try:
1363 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1364 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001365 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1366 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001367
Martin Kellye4e94d22017-03-21 16:05:12 -07001368 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001369 """Perform only the local IO portion of the sync process.
1370 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001371 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001372 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001373 all_refs = self.bare_ref.all
1374 self.CleanPublishedCache(all_refs)
1375 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001376
David Pursehouse1d947b32012-10-25 12:23:11 +09001377 def _doff():
1378 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001379 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001380
Martin Kellye4e94d22017-03-21 16:05:12 -07001381 def _dosubmodules():
1382 self._SyncSubmodules(quiet=True)
1383
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001384 head = self.work_git.GetHead()
1385 if head.startswith(R_HEADS):
1386 branch = head[len(R_HEADS):]
1387 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001388 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001389 except KeyError:
1390 head = None
1391 else:
1392 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001393
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001394 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001395 # Currently on a detached HEAD. The user is assumed to
1396 # not have any local modifications worth worrying about.
1397 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001398 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001399 syncbuf.fail(self, _PriorSyncFailedError())
1400 return
1401
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001402 if head == revid:
1403 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001404 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001405 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001406 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001407 # The copy/linkfile config may have changed.
1408 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001409 return
1410 else:
1411 lost = self._revlist(not_rev(revid), HEAD)
1412 if lost:
1413 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001414
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001415 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001416 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001417 if submodules:
1418 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001419 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001420 syncbuf.fail(self, e)
1421 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001422 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001423 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001424
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001425 if head == revid:
1426 # No changes; don't do anything further.
1427 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001428 # The copy/linkfile config may have changed.
1429 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001430 return
1431
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001432 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001433
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001434 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001435 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001436 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001437 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001438 syncbuf.info(self,
1439 "leaving %s; does not track upstream",
1440 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001441 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001442 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001443 if submodules:
1444 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001445 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001446 syncbuf.fail(self, e)
1447 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001448 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001449 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001450
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001451 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001452 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001453 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001454 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 if not_merged:
1456 if upstream_gain:
1457 # The user has published this branch and some of those
1458 # commits are not yet merged upstream. We do not want
1459 # to rewrite the published commits so we punt.
1460 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001461 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001462 "branch %s is published (but not merged) and is now "
1463 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001464 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001465 elif pub == head:
1466 # All published commits are merged, and thus we are a
1467 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001468 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001469 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001470 if submodules:
1471 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001472 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001473
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001474 # Examine the local commits not in the remote. Find the
1475 # last one attributed to this user, if any.
1476 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001477 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001478 last_mine = None
1479 cnt_mine = 0
1480 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301481 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001482 if committer_email == self.UserEmail:
1483 last_mine = commit_id
1484 cnt_mine += 1
1485
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001486 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001487 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001488
1489 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001490 syncbuf.fail(self, _DirtyError())
1491 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001492
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001493 # If the upstream switched on us, warn the user.
1494 #
1495 if branch.merge != self.revisionExpr:
1496 if branch.merge and self.revisionExpr:
1497 syncbuf.info(self,
1498 'manifest switched %s...%s',
1499 branch.merge,
1500 self.revisionExpr)
1501 elif branch.merge:
1502 syncbuf.info(self,
1503 'manifest no longer tracks %s',
1504 branch.merge)
1505
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001506 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001507 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001508 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001509 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001510 syncbuf.info(self,
1511 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001512 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001513
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001514 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001515 if not ID_RE.match(self.revisionExpr):
1516 # in case of manifest sync the revisionExpr might be a SHA1
1517 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001518 if not branch.merge.startswith('refs/'):
1519 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001520 branch.Save()
1521
Mike Pontillod3153822012-02-28 11:53:24 -08001522 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001523 def _docopyandlink():
1524 self._CopyAndLinkFiles()
1525
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001526 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001527 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001528 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001529 if submodules:
1530 syncbuf.later2(self, _dosubmodules)
1531 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001532 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001533 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001534 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001535 if submodules:
1536 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001537 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001538 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001539 syncbuf.fail(self, e)
1540 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001542 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001543 if submodules:
1544 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001545
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001546 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001547 # dest should already be an absolute path, but src is project relative
1548 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001549 abssrc = os.path.join(self.worktree, src)
1550 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001551
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001552 def AddLinkFile(self, src, dest, absdest):
1553 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001554 # make src relative path to dest
1555 absdestdir = os.path.dirname(absdest)
1556 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001557 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001558
James W. Mills24c13082012-04-12 15:04:13 -05001559 def AddAnnotation(self, name, value, keep):
1560 self.annotations.append(_Annotation(name, value, keep))
1561
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001562 def DownloadPatchSet(self, change_id, patch_id):
1563 """Download a single patch set of a single change to FETCH_HEAD.
1564 """
1565 remote = self.GetRemote(self.remote.name)
1566
1567 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001568 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001569 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001570 if GitCommand(self, cmd, bare=True).Wait() != 0:
1571 return None
1572 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001573 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001574 change_id,
1575 patch_id,
1576 self.bare_git.rev_parse('FETCH_HEAD'))
1577
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001578
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001579# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001580
Simran Basib9a1b732015-08-20 12:19:28 -07001581 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582 """Create a new branch off the manifest's revision.
1583 """
Simran Basib9a1b732015-08-20 12:19:28 -07001584 if not branch_merge:
1585 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001586 head = self.work_git.GetHead()
1587 if head == (R_HEADS + name):
1588 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001589
David Pursehouse8a68ff92012-09-24 12:15:13 +09001590 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001591 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001592 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001593 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001594 capture_stdout=True,
1595 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001596
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001597 branch = self.GetBranch(name)
1598 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001599 branch.merge = branch_merge
1600 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1601 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001602 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001603
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001604 if head.startswith(R_HEADS):
1605 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001606 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001607 except KeyError:
1608 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001609 if revid and head and revid == head:
1610 ref = os.path.join(self.gitdir, R_HEADS + name)
1611 try:
1612 os.makedirs(os.path.dirname(ref))
1613 except OSError:
1614 pass
1615 _lwrite(ref, '%s\n' % revid)
1616 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1617 'ref: %s%s\n' % (R_HEADS, name))
1618 branch.Save()
1619 return True
1620
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001621 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001622 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001623 capture_stdout=True,
1624 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001625 branch.Save()
1626 return True
1627 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001628
Wink Saville02d79452009-04-10 13:01:24 -07001629 def CheckoutBranch(self, name):
1630 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001631
1632 Args:
1633 name: The name of the branch to checkout.
1634
1635 Returns:
1636 True if the checkout succeeded; False if it didn't; None if the branch
1637 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001638 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001639 rev = R_HEADS + name
1640 head = self.work_git.GetHead()
1641 if head == rev:
1642 # Already on the branch
1643 #
1644 return True
Wink Saville02d79452009-04-10 13:01:24 -07001645
David Pursehouse8a68ff92012-09-24 12:15:13 +09001646 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001647 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001648 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001649 except KeyError:
1650 # Branch does not exist in this project
1651 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001652 return None
Wink Saville02d79452009-04-10 13:01:24 -07001653
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001654 if head.startswith(R_HEADS):
1655 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001656 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001657 except KeyError:
1658 head = None
1659
1660 if head == revid:
1661 # Same revision; just update HEAD to point to the new
1662 # target branch, but otherwise take no other action.
1663 #
1664 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1665 'ref: %s%s\n' % (R_HEADS, name))
1666 return True
1667
1668 return GitCommand(self,
1669 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001670 capture_stdout=True,
1671 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001672
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001673 def AbandonBranch(self, name):
1674 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001675
1676 Args:
1677 name: The name of the branch to abandon.
1678
1679 Returns:
1680 True if the abandon succeeded; False if it didn't; None if the branch
1681 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001682 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001683 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001684 all_refs = self.bare_ref.all
1685 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001686 # Doesn't exist
1687 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001688
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001689 head = self.work_git.GetHead()
1690 if head == rev:
1691 # We can't destroy the branch while we are sitting
1692 # on it. Switch to a detached HEAD.
1693 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001694 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001695
David Pursehouse8a68ff92012-09-24 12:15:13 +09001696 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001697 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001698 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1699 '%s\n' % revid)
1700 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001701 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001702
1703 return GitCommand(self,
1704 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001705 capture_stdout=True,
1706 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001707
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001708 def PruneHeads(self):
1709 """Prune any topic branches already merged into upstream.
1710 """
1711 cb = self.CurrentBranch
1712 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001713 left = self._allrefs
1714 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001715 if name.startswith(R_HEADS):
1716 name = name[len(R_HEADS):]
1717 if cb is None or name != cb:
1718 kill.append(name)
1719
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001720 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001721 if cb is not None \
1722 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001723 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001724 self.work_git.DetachHead(HEAD)
1725 kill.append(cb)
1726
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001728 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001730 try:
1731 self.bare_git.DetachHead(rev)
1732
1733 b = ['branch', '-d']
1734 b.extend(kill)
1735 b = GitCommand(self, b, bare=True,
1736 capture_stdout=True,
1737 capture_stderr=True)
1738 b.Wait()
1739 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001740 if ID_RE.match(old):
1741 self.bare_git.DetachHead(old)
1742 else:
1743 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001744 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001745
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001746 for branch in kill:
1747 if (R_HEADS + branch) not in left:
1748 self.CleanPublishedCache()
1749 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001750
1751 if cb and cb not in kill:
1752 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001753 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001754
1755 kept = []
1756 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001757 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758 branch = self.GetBranch(branch)
1759 base = branch.LocalMerge
1760 if not base:
1761 base = rev
1762 kept.append(ReviewableBranch(self, branch, base))
1763 return kept
1764
1765
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001766# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001767
1768 def GetRegisteredSubprojects(self):
1769 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001770
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001771 def rec(subprojects):
1772 if not subprojects:
1773 return
1774 result.extend(subprojects)
1775 for p in subprojects:
1776 rec(p.subprojects)
1777 rec(self.subprojects)
1778 return result
1779
1780 def _GetSubmodules(self):
1781 # Unfortunately we cannot call `git submodule status --recursive` here
1782 # because the working tree might not exist yet, and it cannot be used
1783 # without a working tree in its current implementation.
1784
1785 def get_submodules(gitdir, rev):
1786 # Parse .gitmodules for submodule sub_paths and sub_urls
1787 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1788 if not sub_paths:
1789 return []
1790 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1791 # revision of submodule repository
1792 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1793 submodules = []
1794 for sub_path, sub_url in zip(sub_paths, sub_urls):
1795 try:
1796 sub_rev = sub_revs[sub_path]
1797 except KeyError:
1798 # Ignore non-exist submodules
1799 continue
1800 submodules.append((sub_rev, sub_path, sub_url))
1801 return submodules
1802
1803 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1804 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001805
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001806 def parse_gitmodules(gitdir, rev):
1807 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1808 try:
Anthony King7bdac712014-07-16 12:56:40 +01001809 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1810 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001811 except GitError:
1812 return [], []
1813 if p.Wait() != 0:
1814 return [], []
1815
1816 gitmodules_lines = []
1817 fd, temp_gitmodules_path = tempfile.mkstemp()
1818 try:
1819 os.write(fd, p.stdout)
1820 os.close(fd)
1821 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001822 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1823 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001824 if p.Wait() != 0:
1825 return [], []
1826 gitmodules_lines = p.stdout.split('\n')
1827 except GitError:
1828 return [], []
1829 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001830 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001831
1832 names = set()
1833 paths = {}
1834 urls = {}
1835 for line in gitmodules_lines:
1836 if not line:
1837 continue
1838 m = re_path.match(line)
1839 if m:
1840 names.add(m.group(1))
1841 paths[m.group(1)] = m.group(2)
1842 continue
1843 m = re_url.match(line)
1844 if m:
1845 names.add(m.group(1))
1846 urls[m.group(1)] = m.group(2)
1847 continue
1848 names = sorted(names)
1849 return ([paths.get(name, '') for name in names],
1850 [urls.get(name, '') for name in names])
1851
1852 def git_ls_tree(gitdir, rev, paths):
1853 cmd = ['ls-tree', rev, '--']
1854 cmd.extend(paths)
1855 try:
Anthony King7bdac712014-07-16 12:56:40 +01001856 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1857 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001858 except GitError:
1859 return []
1860 if p.Wait() != 0:
1861 return []
1862 objects = {}
1863 for line in p.stdout.split('\n'):
1864 if not line.strip():
1865 continue
1866 object_rev, object_path = line.split()[2:4]
1867 objects[object_path] = object_rev
1868 return objects
1869
1870 try:
1871 rev = self.GetRevisionId()
1872 except GitError:
1873 return []
1874 return get_submodules(self.gitdir, rev)
1875
1876 def GetDerivedSubprojects(self):
1877 result = []
1878 if not self.Exists:
1879 # If git repo does not exist yet, querying its submodules will
1880 # mess up its states; so return here.
1881 return result
1882 for rev, path, url in self._GetSubmodules():
1883 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001884 relpath, worktree, gitdir, objdir = \
1885 self.manifest.GetSubprojectPaths(self, name, path)
1886 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001887 if project:
1888 result.extend(project.GetDerivedSubprojects())
1889 continue
David James8d201162013-10-11 17:03:19 -07001890
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001891 if url.startswith('..'):
1892 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001893 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001894 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001895 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001896 review=self.remote.review,
1897 revision=self.remote.revision)
1898 subproject = Project(manifest=self.manifest,
1899 name=name,
1900 remote=remote,
1901 gitdir=gitdir,
1902 objdir=objdir,
1903 worktree=worktree,
1904 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001905 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001906 revisionId=rev,
1907 rebase=self.rebase,
1908 groups=self.groups,
1909 sync_c=self.sync_c,
1910 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001911 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001912 parent=self,
1913 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001914 result.append(subproject)
1915 result.extend(subproject.GetDerivedSubprojects())
1916 return result
1917
1918
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001919# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001920 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001921 try:
1922 # if revision (sha or tag) is not present then following function
1923 # throws an error.
1924 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1925 return True
1926 except GitError:
1927 # There is no such persistent revision. We have to fetch it.
1928 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001929
Julien Campergue335f5ef2013-10-16 11:02:35 +02001930 def _FetchArchive(self, tarpath, cwd=None):
1931 cmd = ['archive', '-v', '-o', tarpath]
1932 cmd.append('--remote=%s' % self.remote.url)
1933 cmd.append('--prefix=%s/' % self.relpath)
1934 cmd.append(self.revisionExpr)
1935
1936 command = GitCommand(self, cmd, cwd=cwd,
1937 capture_stdout=True,
1938 capture_stderr=True)
1939
1940 if command.Wait() != 0:
1941 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1942
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001943 def _RemoteFetch(self, name=None,
1944 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001945 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001946 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001947 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001948 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001949 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001950 depth=None,
1951 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001952
1953 is_sha1 = False
1954 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001955 # The depth should not be used when fetching to a mirror because
1956 # it will result in a shallow repository that cannot be cloned or
1957 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001958 # The repo project should also never be synced with partial depth.
1959 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1960 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001961
Shawn Pearce69e04d82014-01-29 12:48:54 -08001962 if depth:
1963 current_branch_only = True
1964
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001965 if ID_RE.match(self.revisionExpr) is not None:
1966 is_sha1 = True
1967
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001968 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001969 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001970 # this is a tag and its sha1 value should never change
1971 tag_name = self.revisionExpr[len(R_TAGS):]
1972
1973 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001974 if self._CheckForImmutableRevision():
1975 print('Skipped fetching project %s (already have persistent ref)'
1976 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001977 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001978 if is_sha1 and not depth:
1979 # When syncing a specific commit and --depth is not set:
1980 # * if upstream is explicitly specified and is not a sha1, fetch only
1981 # upstream as users expect only upstream to be fetch.
1982 # Note: The commit might not be in upstream in which case the sync
1983 # will fail.
1984 # * otherwise, fetch all branches to make sure we end up with the
1985 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001986 if self.upstream:
1987 current_branch_only = not ID_RE.match(self.upstream)
1988 else:
1989 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001990
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001991 if not name:
1992 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001993
1994 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001995 remote = self.GetRemote(name)
1996 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001997 ssh_proxy = True
1998
Shawn O. Pearce88443382010-10-08 10:02:09 +02001999 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002000 if alt_dir and 'objects' == os.path.basename(alt_dir):
2001 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002002 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2003 remote = self.GetRemote(name)
2004
David Pursehouse8a68ff92012-09-24 12:15:13 +09002005 all_refs = self.bare_ref.all
2006 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002007 tmp = set()
2008
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302009 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002010 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002011 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002012 all_refs[r] = ref_id
2013 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 continue
2015
David Pursehouse8a68ff92012-09-24 12:15:13 +09002016 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002017 continue
2018
David Pursehouse8a68ff92012-09-24 12:15:13 +09002019 r = 'refs/_alt/%s' % ref_id
2020 all_refs[r] = ref_id
2021 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002022 tmp.add(r)
2023
heping3d7bbc92017-04-12 19:51:47 +08002024 tmp_packed_lines = []
2025 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002026
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302027 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002028 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002029 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002030 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002031 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002032
heping3d7bbc92017-04-12 19:51:47 +08002033 tmp_packed = ''.join(tmp_packed_lines)
2034 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002035 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002036 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002037 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002039 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002040
Conley Owensf97e8382015-01-21 11:12:46 -08002041 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002042 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002043 else:
2044 # If this repo has shallow objects, then we don't know which refs have
2045 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2046 # do this with projects that don't have shallow objects, since it is less
2047 # efficient.
2048 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2049 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002050
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002051 if quiet:
2052 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002053 if not self.worktree:
2054 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002055 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002056
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002057 # If using depth then we should not get all the tags since they may
2058 # be outside of the depth.
2059 if no_tags or depth:
2060 cmd.append('--no-tags')
2061 else:
2062 cmd.append('--tags')
2063
David Pursehouse74cfd272015-10-14 10:50:15 +09002064 if prune:
2065 cmd.append('--prune')
2066
Martin Kellye4e94d22017-03-21 16:05:12 -07002067 if submodules:
2068 cmd.append('--recurse-submodules=on-demand')
2069
Conley Owens80b87fe2014-05-09 17:13:44 -07002070 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002071 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002072 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002073 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002074 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002075 spec.append('tag')
2076 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002077
David Pursehouse403b64e2015-04-27 10:41:33 +09002078 if not self.manifest.IsMirror:
2079 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002080 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002081 # Shallow checkout of a specific commit, fetch from that commit and not
2082 # the heads only as the commit might be deeper in the history.
2083 spec.append(branch)
2084 else:
2085 if is_sha1:
2086 branch = self.upstream
2087 if branch is not None and branch.strip():
2088 if not branch.startswith('refs/'):
2089 branch = R_HEADS + branch
2090 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002091 cmd.extend(spec)
2092
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002093 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002094 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002095 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002096 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002097 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002098 ok = True
2099 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002100 # If needed, run the 'git remote prune' the first time through the loop
2101 elif (not _i and
2102 "error:" in gitcmd.stderr and
2103 "git remote prune" in gitcmd.stderr):
2104 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002105 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002106 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002107 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002108 break
2109 continue
Brian Harring14a66742012-09-28 20:21:57 -07002110 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002111 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2112 # in sha1 mode, we just tried sync'ing from the upstream field; it
2113 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002114 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002115 elif ret < 0:
2116 # Git died with a signal, exit immediately
2117 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002118 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002119
2120 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002121 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002122 if old_packed != '':
2123 _lwrite(packed_refs, old_packed)
2124 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002125 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002126 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002127
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002128 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002129 # We just synced the upstream given branch; verify we
2130 # got what we wanted, else trigger a second run of all
2131 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002132 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002133 if current_branch_only and depth:
2134 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002135 return self._RemoteFetch(name=name,
2136 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002137 initial=False, quiet=quiet, alt_dir=alt_dir,
2138 depth=None)
2139 else:
2140 # Avoid infinite recursion: sync all branches with depth set to None
2141 return self._RemoteFetch(name=name, current_branch_only=False,
2142 initial=False, quiet=quiet, alt_dir=alt_dir,
2143 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002144
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002145 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002146
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002147 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002148 if initial and \
2149 (self.manifest.manifestProject.config.GetString('repo.depth') or
2150 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002151 return False
2152
2153 remote = self.GetRemote(self.remote.name)
2154 bundle_url = remote.url + '/clone.bundle'
2155 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002156 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2157 'persistent-http',
2158 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002159 return False
2160
2161 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2162 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2163
2164 exist_dst = os.path.exists(bundle_dst)
2165 exist_tmp = os.path.exists(bundle_tmp)
2166
2167 if not initial and not exist_dst and not exist_tmp:
2168 return False
2169
2170 if not exist_dst:
2171 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2172 if not exist_dst:
2173 return False
2174
2175 cmd = ['fetch']
2176 if quiet:
2177 cmd.append('--quiet')
2178 if not self.worktree:
2179 cmd.append('--update-head-ok')
2180 cmd.append(bundle_dst)
2181 for f in remote.fetch:
2182 cmd.append(str(f))
2183 cmd.append('refs/tags/*:refs/tags/*')
2184
2185 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002186 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002187 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002188 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002189 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002190 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002191
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002192 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002193 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002194 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002195
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002196 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002197 if quiet:
2198 cmd += ['--silent']
2199 if os.path.exists(tmpPath):
2200 size = os.stat(tmpPath).st_size
2201 if size >= 1024:
2202 cmd += ['--continue-at', '%d' % (size,)]
2203 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002204 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002205 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2206 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002207 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002208 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002209 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002210 if srcUrl.startswith('persistent-'):
2211 srcUrl = srcUrl[len('persistent-'):]
2212 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002213
Dave Borowitz137d0132015-01-02 11:12:54 -08002214 if IsTrace():
2215 Trace('%s', ' '.join(cmd))
2216 try:
2217 proc = subprocess.Popen(cmd)
2218 except OSError:
2219 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002220
Dave Borowitz137d0132015-01-02 11:12:54 -08002221 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002222
Dave Borowitz137d0132015-01-02 11:12:54 -08002223 if curlret == 22:
2224 # From curl man page:
2225 # 22: HTTP page not retrieved. The requested url was not found or
2226 # returned another error with the HTTP error code being 400 or above.
2227 # This return code only appears if -f, --fail is used.
2228 if not quiet:
2229 print("Server does not provide clone.bundle; ignoring.",
2230 file=sys.stderr)
2231 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002232
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002233 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002234 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002235 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002236 return True
2237 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002238 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002239 return False
2240 else:
2241 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002242
Kris Giesingc8d882a2014-12-23 13:02:32 -08002243 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002244 try:
2245 with open(path) as f:
2246 if f.read(16) == '# v2 git bundle\n':
2247 return True
2248 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002249 if not quiet:
2250 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002251 return False
2252 except OSError:
2253 return False
2254
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002255 def _Checkout(self, rev, quiet=False):
2256 cmd = ['checkout']
2257 if quiet:
2258 cmd.append('-q')
2259 cmd.append(rev)
2260 cmd.append('--')
2261 if GitCommand(self, cmd).Wait() != 0:
2262 if self._allrefs:
2263 raise GitError('%s checkout %s ' % (self.name, rev))
2264
Anthony King7bdac712014-07-16 12:56:40 +01002265 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002266 cmd = ['cherry-pick']
2267 cmd.append(rev)
2268 cmd.append('--')
2269 if GitCommand(self, cmd).Wait() != 0:
2270 if self._allrefs:
2271 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2272
Akshay Vermacf7c0832018-03-15 21:56:30 +05302273 def _LsRemote(self):
2274 cmd = ['ls-remote']
2275 p = GitCommand(self, cmd, capture_stdout=True)
2276 if p.Wait() == 0:
2277 if hasattr(p.stdout, 'decode'):
2278 return p.stdout.decode('utf-8')
2279 else:
2280 return p.stdout
2281 return None
2282
Anthony King7bdac712014-07-16 12:56:40 +01002283 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002284 cmd = ['revert']
2285 cmd.append('--no-edit')
2286 cmd.append(rev)
2287 cmd.append('--')
2288 if GitCommand(self, cmd).Wait() != 0:
2289 if self._allrefs:
2290 raise GitError('%s revert %s ' % (self.name, rev))
2291
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002292 def _ResetHard(self, rev, quiet=True):
2293 cmd = ['reset', '--hard']
2294 if quiet:
2295 cmd.append('-q')
2296 cmd.append(rev)
2297 if GitCommand(self, cmd).Wait() != 0:
2298 raise GitError('%s reset --hard %s ' % (self.name, rev))
2299
Martin Kellye4e94d22017-03-21 16:05:12 -07002300 def _SyncSubmodules(self, quiet=True):
2301 cmd = ['submodule', 'update', '--init', '--recursive']
2302 if quiet:
2303 cmd.append('-q')
2304 if GitCommand(self, cmd).Wait() != 0:
2305 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2306
Anthony King7bdac712014-07-16 12:56:40 +01002307 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002308 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002309 if onto is not None:
2310 cmd.extend(['--onto', onto])
2311 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002312 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002313 raise GitError('%s rebase %s ' % (self.name, upstream))
2314
Pierre Tardy3d125942012-05-04 12:18:12 +02002315 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002316 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002317 if ffonly:
2318 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002319 if GitCommand(self, cmd).Wait() != 0:
2320 raise GitError('%s merge %s ' % (self.name, head))
2321
Kevin Degiabaa7f32014-11-12 11:27:45 -07002322 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002323 init_git_dir = not os.path.exists(self.gitdir)
2324 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002325 try:
2326 # Initialize the bare repository, which contains all of the objects.
2327 if init_obj_dir:
2328 os.makedirs(self.objdir)
2329 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002330
Kevin Degib1a07b82015-07-27 13:33:43 -06002331 # If we have a separate directory to hold refs, initialize it as well.
2332 if self.objdir != self.gitdir:
2333 if init_git_dir:
2334 os.makedirs(self.gitdir)
2335
2336 if init_obj_dir or init_git_dir:
2337 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2338 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002339 try:
2340 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2341 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002342 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002343 print("Retrying clone after deleting %s" %
2344 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002345 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002346 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2347 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002348 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002349 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002350 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2351 except:
2352 raise e
2353 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002354
Kevin Degi384b3c52014-10-16 16:02:58 -06002355 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002356 mp = self.manifest.manifestProject
2357 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002358
Kevin Degib1a07b82015-07-27 13:33:43 -06002359 if ref_dir or mirror_git:
2360 if not mirror_git:
2361 mirror_git = os.path.join(ref_dir, self.name + '.git')
2362 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2363 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002364
Kevin Degib1a07b82015-07-27 13:33:43 -06002365 if os.path.exists(mirror_git):
2366 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002367
Kevin Degib1a07b82015-07-27 13:33:43 -06002368 elif os.path.exists(repo_git):
2369 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002370
Kevin Degib1a07b82015-07-27 13:33:43 -06002371 else:
2372 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002373
Kevin Degib1a07b82015-07-27 13:33:43 -06002374 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002375 if not os.path.isabs(ref_dir):
2376 # The alternate directory is relative to the object database.
2377 ref_dir = os.path.relpath(ref_dir,
2378 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002379 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2380 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002381
Kevin Degib1a07b82015-07-27 13:33:43 -06002382 self._UpdateHooks()
2383
2384 m = self.manifest.manifestProject.config
2385 for key in ['user.name', 'user.email']:
2386 if m.Has(key, include_defaults=False):
2387 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002388 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002389 if self.manifest.IsMirror:
2390 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002391 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002392 self.config.SetString('core.bare', None)
2393 except Exception:
2394 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002395 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002396 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002397 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002398 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002399
Jimmie Westera0444582012-10-24 13:44:42 +02002400 def _UpdateHooks(self):
2401 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002402 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002403
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002404 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002405 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002406 if not os.path.exists(hooks):
2407 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002408 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002409 name = os.path.basename(stock_hook)
2410
Victor Boivie65e0f352011-04-18 11:23:29 +02002411 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002412 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002413 # Don't install a Gerrit Code Review hook if this
2414 # project does not appear to use it for reviews.
2415 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002416 # Since the manifest project is one of those, but also
2417 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002418 continue
2419
2420 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002421 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002422 continue
2423 if os.path.exists(dst):
2424 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002425 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002426 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002427 _warn("%s: Not replacing locally modified %s hook",
2428 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002429 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002430 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002431 platform_utils.symlink(
2432 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002433 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002434 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002435 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002436 else:
2437 raise
2438
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002439 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002440 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002441 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002442 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002443 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002444 remote.review = self.remote.review
2445 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002446
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002447 if self.worktree:
2448 remote.ResetFetch(mirror=False)
2449 else:
2450 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002451 remote.Save()
2452
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002453 def _InitMRef(self):
2454 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002455 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002456
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002457 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002458 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002459
2460 def _InitAnyMRef(self, ref):
2461 cur = self.bare_ref.symref(ref)
2462
2463 if self.revisionId:
2464 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2465 msg = 'manifest set to %s' % self.revisionId
2466 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002467 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002468 else:
2469 remote = self.GetRemote(self.remote.name)
2470 dst = remote.ToLocal(self.revisionExpr)
2471 if cur != dst:
2472 msg = 'manifest set to %s' % self.revisionExpr
2473 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002474
Kevin Degi384b3c52014-10-16 16:02:58 -06002475 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002476 symlink_files = self.shareable_files[:]
2477 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002478 if share_refs:
2479 symlink_files += self.working_tree_files
2480 symlink_dirs += self.working_tree_dirs
2481 to_symlink = symlink_files + symlink_dirs
2482 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002483 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002484 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002485 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002486 # Fail if the links are pointing to the wrong place
2487 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002488 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002489 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002490 'work tree. If you\'re comfortable with the '
2491 'possibility of losing the work tree\'s git metadata,'
2492 ' use `repo sync --force-sync {0}` to '
2493 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002494
David James8d201162013-10-11 17:03:19 -07002495 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2496 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2497
2498 Args:
2499 gitdir: The bare git repository. Must already be initialized.
2500 dotgit: The repository you would like to initialize.
2501 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2502 Only one work tree can store refs under a given |gitdir|.
2503 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2504 This saves you the effort of initializing |dotgit| yourself.
2505 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002506 symlink_files = self.shareable_files[:]
2507 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002508 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002509 symlink_files += self.working_tree_files
2510 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002511 to_symlink = symlink_files + symlink_dirs
2512
2513 to_copy = []
2514 if copy_all:
2515 to_copy = os.listdir(gitdir)
2516
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002517 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002518 for name in set(to_copy).union(to_symlink):
2519 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002520 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002521 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002522
Kevin Degi384b3c52014-10-16 16:02:58 -06002523 if os.path.lexists(dst):
2524 continue
David James8d201162013-10-11 17:03:19 -07002525
2526 # If the source dir doesn't exist, create an empty dir.
2527 if name in symlink_dirs and not os.path.lexists(src):
2528 os.makedirs(src)
2529
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002530 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002531 platform_utils.symlink(
2532 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002533 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002534 if os.path.isdir(src):
2535 shutil.copytree(src, dst)
2536 elif os.path.isfile(src):
2537 shutil.copy(src, dst)
2538
Conley Owens80b87fe2014-05-09 17:13:44 -07002539 # If the source file doesn't exist, ensure the destination
2540 # file doesn't either.
2541 if name in symlink_files and not os.path.lexists(src):
2542 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002543 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002544 except OSError:
2545 pass
2546
David James8d201162013-10-11 17:03:19 -07002547 except OSError as e:
2548 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002549 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002550 else:
2551 raise
2552
Martin Kellye4e94d22017-03-21 16:05:12 -07002553 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002554 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002555 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002556 try:
2557 if init_dotgit:
2558 os.makedirs(dotgit)
2559 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2560 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002561
Kevin Degiabaa7f32014-11-12 11:27:45 -07002562 try:
2563 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2564 except GitError as e:
2565 if force_sync:
2566 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002567 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002568 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002569 except:
2570 raise e
2571 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002572
Kevin Degib1a07b82015-07-27 13:33:43 -06002573 if init_dotgit:
2574 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002575
Kevin Degib1a07b82015-07-27 13:33:43 -06002576 cmd = ['read-tree', '--reset', '-u']
2577 cmd.append('-v')
2578 cmd.append(HEAD)
2579 if GitCommand(self, cmd).Wait() != 0:
2580 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002581
Martin Kellye4e94d22017-03-21 16:05:12 -07002582 if submodules:
2583 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002584 self._CopyAndLinkFiles()
2585 except Exception:
2586 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002587 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002588 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002589
Renaud Paquay788e9622017-01-27 11:41:12 -08002590 def _get_symlink_error_message(self):
2591 if platform_utils.isWindows():
2592 return ('Unable to create symbolic link. Please re-run the command as '
2593 'Administrator, or see '
2594 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2595 'for other options.')
2596 return 'filesystem must support symlinks'
2597
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002598 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002599 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002600
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002601 def _revlist(self, *args, **kw):
2602 a = []
2603 a.extend(args)
2604 a.append('--')
2605 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002606
2607 @property
2608 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002609 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002610
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002611 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002612 """Get logs between two revisions of this project."""
2613 comp = '..'
2614 if rev1:
2615 revs = [rev1]
2616 if rev2:
2617 revs.extend([comp, rev2])
2618 cmd = ['log', ''.join(revs)]
2619 out = DiffColoring(self.config)
2620 if out.is_on and color:
2621 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002622 if pretty_format is not None:
2623 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002624 if oneline:
2625 cmd.append('--oneline')
2626
2627 try:
2628 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2629 if log.Wait() == 0:
2630 return log.stdout
2631 except GitError:
2632 # worktree may not exist if groups changed for example. In that case,
2633 # try in gitdir instead.
2634 if not os.path.exists(self.worktree):
2635 return self.bare_git.log(*cmd[1:])
2636 else:
2637 raise
2638 return None
2639
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002640 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2641 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002642 """Get the list of logs from this revision to given revisionId"""
2643 logs = {}
2644 selfId = self.GetRevisionId(self._allrefs)
2645 toId = toProject.GetRevisionId(toProject._allrefs)
2646
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002647 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2648 pretty_format=pretty_format)
2649 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2650 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002651 return logs
2652
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002653 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002654
David James8d201162013-10-11 17:03:19 -07002655 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002656 self._project = project
2657 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002658 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002659
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002660 def LsOthers(self):
2661 p = GitCommand(self._project,
2662 ['ls-files',
2663 '-z',
2664 '--others',
2665 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002666 bare=False,
David James8d201162013-10-11 17:03:19 -07002667 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002668 capture_stdout=True,
2669 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002670 if p.Wait() == 0:
2671 out = p.stdout
2672 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002673 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002674 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002675 return []
2676
2677 def DiffZ(self, name, *args):
2678 cmd = [name]
2679 cmd.append('-z')
2680 cmd.extend(args)
2681 p = GitCommand(self._project,
2682 cmd,
David James8d201162013-10-11 17:03:19 -07002683 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002684 bare=False,
2685 capture_stdout=True,
2686 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002687 try:
2688 out = p.process.stdout.read()
2689 r = {}
2690 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002691 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002692 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002693 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002694 info = next(out)
2695 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002696 except StopIteration:
2697 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002698
2699 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002700
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002701 def __init__(self, path, omode, nmode, oid, nid, state):
2702 self.path = path
2703 self.src_path = None
2704 self.old_mode = omode
2705 self.new_mode = nmode
2706 self.old_id = oid
2707 self.new_id = nid
2708
2709 if len(state) == 1:
2710 self.status = state
2711 self.level = None
2712 else:
2713 self.status = state[:1]
2714 self.level = state[1:]
2715 while self.level.startswith('0'):
2716 self.level = self.level[1:]
2717
2718 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002719 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002720 if info.status in ('R', 'C'):
2721 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002722 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002723 r[info.path] = info
2724 return r
2725 finally:
2726 p.Wait()
2727
2728 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002729 if self._bare:
2730 path = os.path.join(self._project.gitdir, HEAD)
2731 else:
2732 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002733 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002734 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002735 except IOError as e:
2736 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002737 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002738 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002739 finally:
2740 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302741 try:
2742 line = line.decode()
2743 except AttributeError:
2744 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002745 if line.startswith('ref: '):
2746 return line[5:-1]
2747 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002748
2749 def SetHead(self, ref, message=None):
2750 cmdv = []
2751 if message is not None:
2752 cmdv.extend(['-m', message])
2753 cmdv.append(HEAD)
2754 cmdv.append(ref)
2755 self.symbolic_ref(*cmdv)
2756
2757 def DetachHead(self, new, message=None):
2758 cmdv = ['--no-deref']
2759 if message is not None:
2760 cmdv.extend(['-m', message])
2761 cmdv.append(HEAD)
2762 cmdv.append(new)
2763 self.update_ref(*cmdv)
2764
2765 def UpdateRef(self, name, new, old=None,
2766 message=None,
2767 detach=False):
2768 cmdv = []
2769 if message is not None:
2770 cmdv.extend(['-m', message])
2771 if detach:
2772 cmdv.append('--no-deref')
2773 cmdv.append(name)
2774 cmdv.append(new)
2775 if old is not None:
2776 cmdv.append(old)
2777 self.update_ref(*cmdv)
2778
2779 def DeleteRef(self, name, old=None):
2780 if not old:
2781 old = self.rev_parse(name)
2782 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002783 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002784
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002785 def rev_list(self, *args, **kw):
2786 if 'format' in kw:
2787 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2788 else:
2789 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002790 cmdv.extend(args)
2791 p = GitCommand(self._project,
2792 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002793 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002794 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002795 capture_stdout=True,
2796 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002797 r = []
2798 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002799 if line[-1] == '\n':
2800 line = line[:-1]
2801 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002802 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002803 raise GitError('%s rev-list %s: %s' %
2804 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002805 return r
2806
2807 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002808 """Allow arbitrary git commands using pythonic syntax.
2809
2810 This allows you to do things like:
2811 git_obj.rev_parse('HEAD')
2812
2813 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2814 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002815 Any other positional arguments will be passed to the git command, and the
2816 following keyword arguments are supported:
2817 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002818
2819 Args:
2820 name: The name of the git command to call. Any '_' characters will
2821 be replaced with '-'.
2822
2823 Returns:
2824 A callable object that will try to call git with the named command.
2825 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002826 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002827
Dave Borowitz091f8932012-10-23 17:01:04 -07002828 def runner(*args, **kwargs):
2829 cmdv = []
2830 config = kwargs.pop('config', None)
2831 for k in kwargs:
2832 raise TypeError('%s() got an unexpected keyword argument %r'
2833 % (name, k))
2834 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002835 if not git_require((1, 7, 2)):
2836 raise ValueError('cannot set config on command line for %s()'
2837 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302838 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002839 cmdv.append('-c')
2840 cmdv.append('%s=%s' % (k, v))
2841 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002842 cmdv.extend(args)
2843 p = GitCommand(self._project,
2844 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002845 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002846 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002847 capture_stdout=True,
2848 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002849 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002850 raise GitError('%s %s: %s' %
2851 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002852 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302853 try:
Conley Owensedd01512013-09-26 12:59:58 -07002854 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302855 except AttributeError:
2856 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2858 return r[:-1]
2859 return r
2860 return runner
2861
2862
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002863class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002864
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002865 def __str__(self):
2866 return 'prior sync failed; rebase still in progress'
2867
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002868
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002869class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002870
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871 def __str__(self):
2872 return 'contains uncommitted changes'
2873
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002874
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002875class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002876
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002877 def __init__(self, project, text):
2878 self.project = project
2879 self.text = text
2880
2881 def Print(self, syncbuf):
2882 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2883 syncbuf.out.nl()
2884
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002885
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002886class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002887
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002888 def __init__(self, project, why):
2889 self.project = project
2890 self.why = why
2891
2892 def Print(self, syncbuf):
2893 syncbuf.out.fail('error: %s/: %s',
2894 self.project.relpath,
2895 str(self.why))
2896 syncbuf.out.nl()
2897
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002898
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002899class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002900
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002901 def __init__(self, project, action):
2902 self.project = project
2903 self.action = action
2904
2905 def Run(self, syncbuf):
2906 out = syncbuf.out
2907 out.project('project %s/', self.project.relpath)
2908 out.nl()
2909 try:
2910 self.action()
2911 out.nl()
2912 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002913 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002914 out.nl()
2915 return False
2916
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002917
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002918class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002919
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002920 def __init__(self, config):
2921 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002922 self.project = self.printer('header', attr='bold')
2923 self.info = self.printer('info')
2924 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002925
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002926
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002927class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002928
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002929 def __init__(self, config, detach_head=False):
2930 self._messages = []
2931 self._failures = []
2932 self._later_queue1 = []
2933 self._later_queue2 = []
2934
2935 self.out = _SyncColoring(config)
2936 self.out.redirect(sys.stderr)
2937
2938 self.detach_head = detach_head
2939 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002940 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002941
2942 def info(self, project, fmt, *args):
2943 self._messages.append(_InfoMessage(project, fmt % args))
2944
2945 def fail(self, project, err=None):
2946 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002947 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002948
2949 def later1(self, project, what):
2950 self._later_queue1.append(_Later(project, what))
2951
2952 def later2(self, project, what):
2953 self._later_queue2.append(_Later(project, what))
2954
2955 def Finish(self):
2956 self._PrintMessages()
2957 self._RunLater()
2958 self._PrintMessages()
2959 return self.clean
2960
David Rileye0684ad2017-04-05 00:02:59 -07002961 def Recently(self):
2962 recent_clean = self.recent_clean
2963 self.recent_clean = True
2964 return recent_clean
2965
2966 def _MarkUnclean(self):
2967 self.clean = False
2968 self.recent_clean = False
2969
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002970 def _RunLater(self):
2971 for q in ['_later_queue1', '_later_queue2']:
2972 if not self._RunQueue(q):
2973 return
2974
2975 def _RunQueue(self, queue):
2976 for m in getattr(self, queue):
2977 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002978 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002979 return False
2980 setattr(self, queue, [])
2981 return True
2982
2983 def _PrintMessages(self):
2984 for m in self._messages:
2985 m.Print(self)
2986 for m in self._failures:
2987 m.Print(self)
2988
2989 self._messages = []
2990 self._failures = []
2991
2992
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002993class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002994
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002995 """A special project housed under .repo.
2996 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002997
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002998 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002999 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003000 manifest=manifest,
3001 name=name,
3002 gitdir=gitdir,
3003 objdir=gitdir,
3004 worktree=worktree,
3005 remote=RemoteSpec('origin'),
3006 relpath='.repo/%s' % name,
3007 revisionExpr='refs/heads/master',
3008 revisionId=None,
3009 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003010
3011 def PreSync(self):
3012 if self.Exists:
3013 cb = self.CurrentBranch
3014 if cb:
3015 base = self.GetBranch(cb).merge
3016 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003017 self.revisionExpr = base
3018 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003019
Martin Kelly224a31a2017-07-10 14:46:25 -07003020 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003021 """ Prepare MetaProject for manifest branch switch
3022 """
3023
3024 # detach and delete manifest branch, allowing a new
3025 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003026 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003027 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003028 syncbuf.Finish()
3029
3030 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003031 ['update-ref', '-d', 'refs/heads/default'],
3032 capture_stdout=True,
3033 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003034
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003035 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003036 def LastFetch(self):
3037 try:
3038 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3039 return os.path.getmtime(fh)
3040 except OSError:
3041 return 0
3042
3043 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003044 def HasChanges(self):
3045 """Has the remote received new commits not yet checked out?
3046 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003047 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003048 return False
3049
David Pursehouse8a68ff92012-09-24 12:15:13 +09003050 all_refs = self.bare_ref.all
3051 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003052 head = self.work_git.GetHead()
3053 if head.startswith(R_HEADS):
3054 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003055 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003056 except KeyError:
3057 head = None
3058
3059 if revid == head:
3060 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003061 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003062 return True
3063 return False