blob: 11c5257c507cc773687797a9a6d5a05a72afed5a [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, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070034 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070036from error import CacheApplyError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080037from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
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:
66 os.rename(lock, path)
67 except OSError:
68 os.remove(lock)
69 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:
106 d = os.path.realpath(os.path.abspath(os.path.dirname(__file__)))
107 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,
179 draft=False,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200180 private=False,
181 wip=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700182 dest_branch=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800183 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700184 people,
Brian Harring435370c2012-07-28 15:37:04 -0700185 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400186 draft=draft,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200187 private=private,
188 wip=wip,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400189 dest_branch=dest_branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700190
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700191 def GetPublishedRefs(self):
192 refs = {}
193 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700194 self.branch.remote.SshReviewUrl(self.project.UserEmail),
195 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700196 for line in output.split('\n'):
197 try:
198 (sha, ref) = line.split()
199 refs[sha] = ref
200 except ValueError:
201 pass
202
203 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700204
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700205
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700206class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700207
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208 def __init__(self, config):
209 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100210 self.project = self.printer('header', attr='bold')
211 self.branch = self.printer('header', attr='bold')
212 self.nobranch = self.printer('nobranch', fg='red')
213 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214
Anthony King7bdac712014-07-16 12:56:40 +0100215 self.added = self.printer('added', fg='green')
216 self.changed = self.printer('changed', fg='red')
217 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
219
220class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700221
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222 def __init__(self, config):
223 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100224 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700225
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700226
Anthony King7bdac712014-07-16 12:56:40 +0100227class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
James W. Mills24c13082012-04-12 15:04:13 -0500229 def __init__(self, name, value, keep):
230 self.name = name
231 self.value = value
232 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700234
Anthony King7bdac712014-07-16 12:56:40 +0100235class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800237 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238 self.src = src
239 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800240 self.abs_src = abssrc
241 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
243 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 src = self.abs_src
245 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 # copy file if it does not exist or is out of date
247 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
248 try:
249 # remove existing file first, since it might be read-only
250 if os.path.exists(dest):
251 os.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400252 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200253 dest_dir = os.path.dirname(dest)
254 if not os.path.isdir(dest_dir):
255 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700256 shutil.copy(src, dest)
257 # make the file read-only
258 mode = os.stat(dest)[stat.ST_MODE]
259 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
260 os.chmod(dest, mode)
261 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700262 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Anthony King7bdac712014-07-16 12:56:40 +0100265class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700266
Wink Saville4c426ef2015-06-03 08:05:17 -0700267 def __init__(self, git_worktree, src, dest, relsrc, absdest):
268 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500269 self.src = src
270 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700271 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500272 self.abs_dest = absdest
273
Wink Saville4c426ef2015-06-03 08:05:17 -0700274 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500275 # link file if it does not exist or is out of date
Wink Saville4c426ef2015-06-03 08:05:17 -0700276 if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 try:
278 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800279 if os.path.lexists(absDest):
Wink Saville4c426ef2015-06-03 08:05:17 -0700280 os.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 if not os.path.isdir(dest_dir):
284 os.makedirs(dest_dir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700285 os.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 _error('Cannot link file %s to %s', relSrc, absDest)
288
289 def _Link(self):
290 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
291 on the src linking all of the files in the source in to the destination
292 directory.
293 """
294 # We use the absSrc to handle the situation where the current directory
295 # is not the root of the repo
296 absSrc = os.path.join(self.git_worktree, self.src)
297 if os.path.exists(absSrc):
298 # Entity exists so just a simple one to one link operation
299 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
300 else:
301 # Entity doesn't exist assume there is a wild card
302 absDestDir = self.abs_dest
303 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
304 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700305 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700306 else:
307 absSrcFiles = glob.glob(absSrc)
308 for absSrcFile in absSrcFiles:
309 # Create a releative path from source dir to destination dir
310 absSrcDir = os.path.dirname(absSrcFile)
311 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
312
313 # Get the source file name
314 srcFile = os.path.basename(absSrcFile)
315
316 # Now form the final full paths to srcFile. They will be
317 # absolute for the desintaiton and relative for the srouce.
318 absDest = os.path.join(absDestDir, srcFile)
319 relSrc = os.path.join(relSrcDir, srcFile)
320 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500321
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700322
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700323class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700324
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700325 def __init__(self,
326 name,
Anthony King7bdac712014-07-16 12:56:40 +0100327 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700328 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100329 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700330 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700331 orig_name=None,
332 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700333 self.name = name
334 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700335 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700336 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100337 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700338 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700339 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700340
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700341
Doug Anderson37282b42011-03-04 11:54:18 -0800342class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700343
Doug Anderson37282b42011-03-04 11:54:18 -0800344 """A RepoHook contains information about a script to run as a hook.
345
346 Hooks are used to run a python script before running an upload (for instance,
347 to run presubmit checks). Eventually, we may have hooks for other actions.
348
349 This shouldn't be confused with files in the 'repo/hooks' directory. Those
350 files are copied into each '.git/hooks' folder for each project. Repo-level
351 hooks are associated instead with repo actions.
352
353 Hooks are always python. When a hook is run, we will load the hook into the
354 interpreter and execute its main() function.
355 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700356
Doug Anderson37282b42011-03-04 11:54:18 -0800357 def __init__(self,
358 hook_type,
359 hooks_project,
360 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400361 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800362 abort_if_user_denies=False):
363 """RepoHook constructor.
364
365 Params:
366 hook_type: A string representing the type of hook. This is also used
367 to figure out the name of the file containing the hook. For
368 example: 'pre-upload'.
369 hooks_project: The project containing the repo hooks. If you have a
370 manifest, this is manifest.repo_hooks_project. OK if this is None,
371 which will make the hook a no-op.
372 topdir: Repo's top directory (the one containing the .repo directory).
373 Scripts will run with CWD as this directory. If you have a manifest,
374 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400375 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800376 abort_if_user_denies: If True, we'll throw a HookError() if the user
377 doesn't allow us to run the hook.
378 """
379 self._hook_type = hook_type
380 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400381 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800382 self._topdir = topdir
383 self._abort_if_user_denies = abort_if_user_denies
384
385 # Store the full path to the script for convenience.
386 if self._hooks_project:
387 self._script_fullpath = os.path.join(self._hooks_project.worktree,
388 self._hook_type + '.py')
389 else:
390 self._script_fullpath = None
391
392 def _GetHash(self):
393 """Return a hash of the contents of the hooks directory.
394
395 We'll just use git to do this. This hash has the property that if anything
396 changes in the directory we will return a different has.
397
398 SECURITY CONSIDERATION:
399 This hash only represents the contents of files in the hook directory, not
400 any other files imported or called by hooks. Changes to imported files
401 can change the script behavior without affecting the hash.
402
403 Returns:
404 A string representing the hash. This will always be ASCII so that it can
405 be printed to the user easily.
406 """
407 assert self._hooks_project, "Must have hooks to calculate their hash."
408
409 # We will use the work_git object rather than just calling GetRevisionId().
410 # That gives us a hash of the latest checked in version of the files that
411 # the user will actually be executing. Specifically, GetRevisionId()
412 # doesn't appear to change even if a user checks out a different version
413 # of the hooks repo (via git checkout) nor if a user commits their own revs.
414 #
415 # NOTE: Local (non-committed) changes will not be factored into this hash.
416 # I think this is OK, since we're really only worried about warning the user
417 # about upstream changes.
418 return self._hooks_project.work_git.rev_parse('HEAD')
419
420 def _GetMustVerb(self):
421 """Return 'must' if the hook is required; 'should' if not."""
422 if self._abort_if_user_denies:
423 return 'must'
424 else:
425 return 'should'
426
427 def _CheckForHookApproval(self):
428 """Check to see whether this hook has been approved.
429
Mike Frysinger40252c22016-08-15 21:23:44 -0400430 We'll accept approval of manifest URLs if they're using secure transports.
431 This way the user can say they trust the manifest hoster. For insecure
432 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800433
434 Note that we ask permission for each individual hook even though we use
435 the hash of all hooks when detecting changes. We'd like the user to be
436 able to approve / deny each hook individually. We only use the hash of all
437 hooks because there is no other easy way to detect changes to local imports.
438
439 Returns:
440 True if this hook is approved to run; False otherwise.
441
442 Raises:
443 HookError: Raised if the user doesn't approve and abort_if_user_denies
444 was passed to the consturctor.
445 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400446 if self._ManifestUrlHasSecureScheme():
447 return self._CheckForHookApprovalManifest()
448 else:
449 return self._CheckForHookApprovalHash()
450
451 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
452 changed_prompt):
453 """Check for approval for a particular attribute and hook.
454
455 Args:
456 subkey: The git config key under [repo.hooks.<hook_type>] to store the
457 last approved string.
458 new_val: The new value to compare against the last approved one.
459 main_prompt: Message to display to the user to ask for approval.
460 changed_prompt: Message explaining why we're re-asking for approval.
461
462 Returns:
463 True if this hook is approved to run; False otherwise.
464
465 Raises:
466 HookError: Raised if the user doesn't approve and abort_if_user_denies
467 was passed to the consturctor.
468 """
Doug Anderson37282b42011-03-04 11:54:18 -0800469 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400470 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800471
Mike Frysinger40252c22016-08-15 21:23:44 -0400472 # Get the last value that the user approved for this hook; may be None.
473 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800474
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800476 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800478 # Approval matched. We're done.
479 return True
480 else:
481 # Give the user a reason why we're prompting, since they last told
482 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400483 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800484 else:
485 prompt = ''
486
487 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
488 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400489 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530490 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900491 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800492
493 # User is doing a one-time approval.
494 if response in ('y', 'yes'):
495 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400496 elif response == 'always':
497 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800498 return True
499
500 # For anything else, we'll assume no approval.
501 if self._abort_if_user_denies:
502 raise HookError('You must allow the %s hook or use --no-verify.' %
503 self._hook_type)
504
505 return False
506
Mike Frysinger40252c22016-08-15 21:23:44 -0400507 def _ManifestUrlHasSecureScheme(self):
508 """Check if the URI for the manifest is a secure transport."""
509 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
510 parse_results = urllib.parse.urlparse(self._manifest_url)
511 return parse_results.scheme in secure_schemes
512
513 def _CheckForHookApprovalManifest(self):
514 """Check whether the user has approved this manifest host.
515
516 Returns:
517 True if this hook is approved to run; False otherwise.
518 """
519 return self._CheckForHookApprovalHelper(
520 'approvedmanifest',
521 self._manifest_url,
522 'Run hook scripts from %s' % (self._manifest_url,),
523 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
524
525 def _CheckForHookApprovalHash(self):
526 """Check whether the user has approved the hooks repo.
527
528 Returns:
529 True if this hook is approved to run; False otherwise.
530 """
531 prompt = ('Repo %s run the script:\n'
532 ' %s\n'
533 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700534 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400535 return self._CheckForHookApprovalHelper(
536 'approvedhash',
537 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700538 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400539 'Scripts have changed since %s was allowed.' % (self._hook_type,))
540
Doug Anderson37282b42011-03-04 11:54:18 -0800541 def _ExecuteHook(self, **kwargs):
542 """Actually execute the given hook.
543
544 This will run the hook's 'main' function in our python interpreter.
545
546 Args:
547 kwargs: Keyword arguments to pass to the hook. These are often specific
548 to the hook type. For instance, pre-upload hooks will contain
549 a project_list.
550 """
551 # Keep sys.path and CWD stashed away so that we can always restore them
552 # upon function exit.
553 orig_path = os.getcwd()
554 orig_syspath = sys.path
555
556 try:
557 # Always run hooks with CWD as topdir.
558 os.chdir(self._topdir)
559
560 # Put the hook dir as the first item of sys.path so hooks can do
561 # relative imports. We want to replace the repo dir as [0] so
562 # hooks can't import repo files.
563 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
564
565 # Exec, storing global context in the context dict. We catch exceptions
566 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500567 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800568 try:
Anthony King70f68902014-05-05 21:15:34 +0100569 exec(compile(open(self._script_fullpath).read(),
570 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800571 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700572 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
573 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800574
575 # Running the script should have defined a main() function.
576 if 'main' not in context:
577 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
578
Doug Anderson37282b42011-03-04 11:54:18 -0800579 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
580 # We don't actually want hooks to define their main with this argument--
581 # it's there to remind them that their hook should always take **kwargs.
582 # For instance, a pre-upload hook should be defined like:
583 # def main(project_list, **kwargs):
584 #
585 # This allows us to later expand the API without breaking old hooks.
586 kwargs = kwargs.copy()
587 kwargs['hook_should_take_kwargs'] = True
588
589 # Call the main function in the hook. If the hook should cause the
590 # build to fail, it will raise an Exception. We'll catch that convert
591 # to a HookError w/ just the failing traceback.
592 try:
593 context['main'](**kwargs)
594 except Exception:
595 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700596 'above.' % (traceback.format_exc(),
597 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800598 finally:
599 # Restore sys.path and CWD.
600 sys.path = orig_syspath
601 os.chdir(orig_path)
602
603 def Run(self, user_allows_all_hooks, **kwargs):
604 """Run the hook.
605
606 If the hook doesn't exist (because there is no hooks project or because
607 this particular hook is not enabled), this is a no-op.
608
609 Args:
610 user_allows_all_hooks: If True, we will never prompt about running the
611 hook--we'll just assume it's OK to run it.
612 kwargs: Keyword arguments to pass to the hook. These are often specific
613 to the hook type. For instance, pre-upload hooks will contain
614 a project_list.
615
616 Raises:
617 HookError: If there was a problem finding the hook or the user declined
618 to run a required hook (from _CheckForHookApproval).
619 """
620 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700621 if ((not self._hooks_project) or (self._hook_type not in
622 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800623 return
624
625 # Bail with a nice error if we can't find the hook.
626 if not os.path.isfile(self._script_fullpath):
627 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
628
629 # Make sure the user is OK with running the hook.
630 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
631 return
632
633 # Run the hook with the same version of python we're using.
634 self._ExecuteHook(**kwargs)
635
636
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700637class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600638 # These objects can be shared between several working trees.
639 shareable_files = ['description', 'info']
640 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
641 # These objects can only be used by a single working tree.
642 working_tree_files = ['config', 'packed-refs', 'shallow']
643 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700644
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700645 def __init__(self,
646 manifest,
647 name,
648 remote,
649 gitdir,
David James8d201162013-10-11 17:03:19 -0700650 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700651 worktree,
652 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700653 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800654 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100655 rebase=True,
656 groups=None,
657 sync_c=False,
658 sync_s=False,
659 clone_depth=None,
660 upstream=None,
661 parent=None,
662 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900663 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700664 optimized_fetch=False,
665 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800666 """Init a Project object.
667
668 Args:
669 manifest: The XmlManifest object.
670 name: The `name` attribute of manifest.xml's project element.
671 remote: RemoteSpec object specifying its remote's properties.
672 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700673 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800674 worktree: Absolute path of git working tree.
675 relpath: Relative path of git working tree to repo's top directory.
676 revisionExpr: The `revision` attribute of manifest.xml's project element.
677 revisionId: git commit id for checking out.
678 rebase: The `rebase` attribute of manifest.xml's project element.
679 groups: The `groups` attribute of manifest.xml's project element.
680 sync_c: The `sync-c` attribute of manifest.xml's project element.
681 sync_s: The `sync-s` attribute of manifest.xml's project element.
682 upstream: The `upstream` attribute of manifest.xml's project element.
683 parent: The parent Project object.
684 is_derived: False if the project was explicitly defined in the manifest;
685 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400686 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900687 optimized_fetch: If True, when a project is set to a sha1 revision, only
688 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700689 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800690 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691 self.manifest = manifest
692 self.name = name
693 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800694 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700695 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800696 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700697 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800698 else:
699 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700701 self.revisionExpr = revisionExpr
702
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700703 if revisionId is None \
704 and revisionExpr \
705 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700706 self.revisionId = revisionExpr
707 else:
708 self.revisionId = revisionId
709
Mike Pontillod3153822012-02-28 11:53:24 -0800710 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700711 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700712 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800713 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900714 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700715 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800716 self.parent = parent
717 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900718 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800720
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700721 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700722 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500723 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500724 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700725 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
726 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800728 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700729 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800730 else:
731 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700732 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700733 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700734 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400735 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700736 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737
Doug Anderson37282b42011-03-04 11:54:18 -0800738 # This will be filled in if a project is later identified to be the
739 # project containing repo hooks.
740 self.enabled_repo_hooks = []
741
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800743 def Derived(self):
744 return self.is_derived
745
746 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600748 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749
750 @property
751 def CurrentBranch(self):
752 """Obtain the name of the currently checked out branch.
753 The branch name omits the 'refs/heads/' prefix.
754 None is returned if the project is on a detached HEAD.
755 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700756 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 if b.startswith(R_HEADS):
758 return b[len(R_HEADS):]
759 return None
760
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700761 def IsRebaseInProgress(self):
762 w = self.worktree
763 g = os.path.join(w, '.git')
764 return os.path.exists(os.path.join(g, 'rebase-apply')) \
765 or os.path.exists(os.path.join(g, 'rebase-merge')) \
766 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200767
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700768 def IsDirty(self, consider_untracked=True):
769 """Is the working directory modified in some way?
770 """
771 self.work_git.update_index('-q',
772 '--unmerged',
773 '--ignore-missing',
774 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900775 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 return True
777 if self.work_git.DiffZ('diff-files'):
778 return True
779 if consider_untracked and self.work_git.LsOthers():
780 return True
781 return False
782
783 _userident_name = None
784 _userident_email = None
785
786 @property
787 def UserName(self):
788 """Obtain the user's personal name.
789 """
790 if self._userident_name is None:
791 self._LoadUserIdentity()
792 return self._userident_name
793
794 @property
795 def UserEmail(self):
796 """Obtain the user's email address. This is very likely
797 to be their Gerrit login.
798 """
799 if self._userident_email is None:
800 self._LoadUserIdentity()
801 return self._userident_email
802
803 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900804 u = self.bare_git.var('GIT_COMMITTER_IDENT')
805 m = re.compile("^(.*) <([^>]*)> ").match(u)
806 if m:
807 self._userident_name = m.group(1)
808 self._userident_email = m.group(2)
809 else:
810 self._userident_name = ''
811 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812
813 def GetRemote(self, name):
814 """Get the configuration for a single remote.
815 """
816 return self.config.GetRemote(name)
817
818 def GetBranch(self, name):
819 """Get the configuration for a single branch.
820 """
821 return self.config.GetBranch(name)
822
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700823 def GetBranches(self):
824 """Get all existing local branches.
825 """
826 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900827 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700829
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530830 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 if name.startswith(R_HEADS):
832 name = name[len(R_HEADS):]
833 b = self.GetBranch(name)
834 b.current = name == current
835 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900836 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837 heads[name] = b
838
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530839 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700840 if name.startswith(R_PUB):
841 name = name[len(R_PUB):]
842 b = heads.get(name)
843 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845
846 return heads
847
Colin Cross5acde752012-03-28 20:15:45 -0700848 def MatchesGroups(self, manifest_groups):
849 """Returns true if the manifest groups specified at init should cause
850 this project to be synced.
851 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700852 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700853
854 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700855 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700856 manifest_groups: "-group1,group2"
857 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500858
859 The special manifest group "default" will match any project that
860 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700861 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500862 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700864 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500865 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700866
Conley Owens971de8e2012-04-16 10:36:08 -0700867 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 for group in expanded_manifest_groups:
869 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700870 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700872 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700873
Conley Owens971de8e2012-04-16 10:36:08 -0700874 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700876# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700877 def UncommitedFiles(self, get_all=True):
878 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700880 Args:
881 get_all: a boolean, if True - get information about all different
882 uncommitted files. If False - return as soon as any kind of
883 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500884 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500886 self.work_git.update_index('-q',
887 '--unmerged',
888 '--ignore-missing',
889 '--refresh')
890 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700891 details.append("rebase in progress")
892 if not get_all:
893 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700895 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
896 if changes:
897 details.extend(changes)
898 if not get_all:
899 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500900
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700901 changes = self.work_git.DiffZ('diff-files').keys()
902 if changes:
903 details.extend(changes)
904 if not get_all:
905 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500906
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700907 changes = self.work_git.LsOthers()
908 if changes:
909 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500910
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700911 return details
912
913 def HasChanges(self):
914 """Returns true if there are uncommitted changes.
915 """
916 if self.UncommitedFiles(get_all=False):
917 return True
918 else:
919 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500920
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600921 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200923
924 Args:
925 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600926 quiet: If True then only print the project name. Do not print
927 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 """
929 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700930 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200931 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700932 print(file=output_redir)
933 print('project %s/' % self.relpath, file=output_redir)
934 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 return
936
937 self.work_git.update_index('-q',
938 '--unmerged',
939 '--ignore-missing',
940 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700941 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
943 df = self.work_git.DiffZ('diff-files')
944 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100945 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700946 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947
948 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700949 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200950 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700951 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600953 if quiet:
954 out.nl()
955 return 'DIRTY'
956
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957 branch = self.CurrentBranch
958 if branch is None:
959 out.nobranch('(*** NO BRANCH ***)')
960 else:
961 out.branch('branch %s', branch)
962 out.nl()
963
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700964 if rb:
965 out.important('prior sync failed; rebase still in progress')
966 out.nl()
967
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 paths = list()
969 paths.extend(di.keys())
970 paths.extend(df.keys())
971 paths.extend(do)
972
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530973 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900974 try:
975 i = di[p]
976 except KeyError:
977 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900979 try:
980 f = df[p]
981 except KeyError:
982 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200983
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 if i:
985 i_status = i.status.upper()
986 else:
987 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 if f:
990 f_status = f.status.lower()
991 else:
992 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
994 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800995 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700996 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997 else:
998 line = ' %s%s\t%s' % (i_status, f_status, p)
999
1000 if i and not f:
1001 out.added('%s', line)
1002 elif (i and f) or (not i and f):
1003 out.changed('%s', line)
1004 elif not i and not f:
1005 out.untracked('%s', line)
1006 else:
1007 out.write('%s', line)
1008 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001009
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001010 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001011
pelyad67872d2012-03-28 14:49:58 +03001012 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001013 """Prints the status of the repository to stdout.
1014 """
1015 out = DiffColoring(self.config)
1016 cmd = ['diff']
1017 if out.is_on:
1018 cmd.append('--color')
1019 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001020 if absolute_paths:
1021 cmd.append('--src-prefix=a/%s/' % self.relpath)
1022 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023 cmd.append('--')
1024 p = GitCommand(self,
1025 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001026 capture_stdout=True,
1027 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028 has_diff = False
1029 for line in p.process.stdout:
1030 if not has_diff:
1031 out.nl()
1032 out.project('project %s/' % self.relpath)
1033 out.nl()
1034 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001035 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 p.Wait()
1037
1038
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001039# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040
David Pursehouse8a68ff92012-09-24 12:15:13 +09001041 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 """Was the branch published (uploaded) for code review?
1043 If so, returns the SHA-1 hash of the last published
1044 state for the branch.
1045 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001046 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001047 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001048 try:
1049 return self.bare_git.rev_parse(key)
1050 except GitError:
1051 return None
1052 else:
1053 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 except KeyError:
1056 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057
David Pursehouse8a68ff92012-09-24 12:15:13 +09001058 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059 """Prunes any stale published refs.
1060 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001061 if all_refs is None:
1062 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 heads = set()
1064 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301065 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 if name.startswith(R_HEADS):
1067 heads.add(name)
1068 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301071 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 n = name[len(R_PUB):]
1073 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001074 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001076 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 """List any branches which can be uploaded for review.
1078 """
1079 heads = {}
1080 pubed = {}
1081
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301082 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001084 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001086 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
1088 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001090 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001092 if selected_branch and branch != selected_branch:
1093 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001095 rb = self.GetUploadableBranch(branch)
1096 if rb:
1097 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 return ready
1099
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001100 def GetUploadableBranch(self, branch_name):
1101 """Get a single uploadable branch, or None.
1102 """
1103 branch = self.GetBranch(branch_name)
1104 base = branch.LocalMerge
1105 if branch.LocalMerge:
1106 rb = ReviewableBranch(self, branch, base)
1107 if rb.commits:
1108 return rb
1109 return None
1110
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001111 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001112 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001113 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001114 draft=False,
Changcheng Xiao7da6f862017-08-02 16:55:03 +02001115 private=False,
1116 wip=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001117 dest_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001118 """Uploads the named branch for code review.
1119 """
1120 if branch is None:
1121 branch = self.CurrentBranch
1122 if branch is None:
1123 raise GitError('not currently on a branch')
1124
1125 branch = self.GetBranch(branch)
1126 if not branch.LocalMerge:
1127 raise GitError('branch %s does not track a remote' % branch.name)
1128 if not branch.remote.review:
1129 raise GitError('remote %s has no review url' % branch.remote.name)
1130
Bryan Jacobsf609f912013-05-06 13:36:24 -04001131 if dest_branch is None:
1132 dest_branch = self.dest_branch
1133 if dest_branch is None:
1134 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001135 if not dest_branch.startswith(R_HEADS):
1136 dest_branch = R_HEADS + dest_branch
1137
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001138 if not branch.remote.projectname:
1139 branch.remote.projectname = self.name
1140 branch.remote.Save()
1141
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001142 url = branch.remote.ReviewUrl(self.UserEmail)
1143 if url is None:
1144 raise UploadError('review not configured')
1145 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001146
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001147 if url.startswith('ssh://'):
Mike Frysingercb099b32018-12-13 13:08:57 -05001148 rp = ['gerrit receive-pack']
1149 for e in people[0]:
1150 rp.append('--reviewer=%s' % sq(e))
1151 for e in people[1]:
1152 rp.append('--cc=%s' % sq(e))
1153 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001154
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001155 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001156
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001157 if dest_branch.startswith(R_HEADS):
1158 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001159
1160 upload_type = 'for'
1161 if draft:
1162 upload_type = 'drafts'
1163
1164 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1165 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 if auto_topic:
1167 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao7da6f862017-08-02 16:55:03 +02001168
Mike Frysingercb099b32018-12-13 13:08:57 -05001169 if not url.startswith('ssh://'):
1170 rp = ['r=%s' % p for p in people[0]] + \
1171 ['cc=%s' % p for p in people[1]]
1172 if private:
1173 rp = rp + ['private']
1174 if wip:
1175 rp = rp + ['wip']
1176 if rp:
1177 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001178 cmd.append(ref_spec)
1179
Anthony King7bdac712014-07-16 12:56:40 +01001180 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001181 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001182
1183 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1184 self.bare_git.UpdateRef(R_PUB + branch.name,
1185 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001186 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
1188
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001189# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001190
Julien Campergue335f5ef2013-10-16 11:02:35 +02001191 def _ExtractArchive(self, tarpath, path=None):
1192 """Extract the given tar on its current location
1193
1194 Args:
1195 - tarpath: The path to the actual tar file
1196
1197 """
1198 try:
1199 with tarfile.open(tarpath, 'r') as tar:
1200 tar.extractall(path=path)
1201 return True
1202 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001203 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001204 return False
1205
Ningning Xiac2fbc782016-08-22 14:24:39 -07001206 def CachePopulate(self, cache_dir, url):
1207 """Populate cache in the cache_dir.
1208
1209 Args:
1210 cache_dir: Directory to cache git files from Google Storage.
1211 url: Git url of current repository.
1212
1213 Raises:
1214 CacheApplyError if it fails to populate the git cache.
1215 """
1216 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1217 '--cache-dir', cache_dir, url]
1218
1219 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1220 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1221 'url: %s' % (cache_dir, url))
1222
1223 def CacheExists(self, cache_dir, url):
1224 """Check the existence of the cache files.
1225
1226 Args:
1227 cache_dir: Directory to cache git files.
1228 url: Git url of current repository.
1229
1230 Raises:
1231 CacheApplyError if the cache files do not exist.
1232 """
1233 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1234
1235 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1236 if exist.Wait() != 0:
1237 raise CacheApplyError('Failed to execute git cache exists cmd. '
1238 'cache_dir: %s url: %s' % (cache_dir, url))
1239
1240 if not exist.stdout or not exist.stdout.strip():
1241 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1242 'url: %s' % (cache_dir, url))
1243 return exist.stdout.strip()
1244
1245 def CacheApply(self, cache_dir):
1246 """Apply git cache files populated from Google Storage buckets.
1247
1248 Args:
1249 cache_dir: Directory to cache git files.
1250
1251 Raises:
1252 CacheApplyError if it fails to apply git caches.
1253 """
1254 remote = self.GetRemote(self.remote.name)
1255
1256 self.CachePopulate(cache_dir, remote.url)
1257
1258 mirror_dir = self.CacheExists(cache_dir, remote.url)
1259
1260 refspec = RefSpec(True, 'refs/heads/*',
1261 'refs/remotes/%s/*' % remote.name)
1262
1263 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1264 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1265 raise CacheApplyError('Failed to fetch refs %s from %s' %
1266 (mirror_dir, str(refspec)))
1267
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001268 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001269 quiet=False,
1270 is_new=None,
1271 current_branch_only=False,
1272 force_sync=False,
1273 clone_bundle=True,
1274 no_tags=False,
1275 archive=False,
1276 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001277 prune=False,
Mike Frysingerde72f6a2018-12-13 03:20:31 -05001278 submodules=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001279 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001280 """Perform only the network IO portion of the sync process.
1281 Local working directory/branch state is not affected.
1282 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001283 if archive and not isinstance(self, MetaProject):
1284 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001285 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001286 return False
1287
1288 name = self.relpath.replace('\\', '/')
1289 name = name.replace('/', '_')
1290 tarpath = '%s.tar' % name
1291 topdir = self.manifest.topdir
1292
1293 try:
1294 self._FetchArchive(tarpath, cwd=topdir)
1295 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001296 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001297 return False
1298
1299 # From now on, we only need absolute tarpath
1300 tarpath = os.path.join(topdir, tarpath)
1301
1302 if not self._ExtractArchive(tarpath, path=topdir):
1303 return False
1304 try:
1305 os.remove(tarpath)
1306 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001307 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001308 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001309 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001310 if is_new is None:
1311 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001312 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001313 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001314 else:
1315 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001316 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001317
1318 if is_new:
1319 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1320 try:
1321 fd = open(alt, 'rb')
1322 try:
1323 alt_dir = fd.readline().rstrip()
1324 finally:
1325 fd.close()
1326 except IOError:
1327 alt_dir = None
1328 else:
1329 alt_dir = None
1330
Ningning Xiac2fbc782016-08-22 14:24:39 -07001331 applied_cache = False
1332 # If cache_dir is provided, and it's a new repository without
1333 # alternative_dir, bootstrap this project repo with the git
1334 # cache files.
1335 if cache_dir is not None and is_new and alt_dir is None:
1336 try:
1337 self.CacheApply(cache_dir)
1338 applied_cache = True
1339 is_new = False
1340 except CacheApplyError as e:
1341 _error('Could not apply git cache: %s', e)
1342 _error('Please check if you have the right GS credentials.')
1343 _error('Please check if the cache files exist in GS.')
1344
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001345 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001346 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001347 and alt_dir is None \
1348 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001349 is_new = False
1350
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001351 if not current_branch_only:
1352 if self.sync_c:
1353 current_branch_only = True
1354 elif not self.manifest._loaded:
1355 # Manifest cannot check defaults until it syncs.
1356 current_branch_only = False
1357 elif self.manifest.default.sync_c:
1358 current_branch_only = True
1359
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001360 if self.clone_depth:
1361 depth = self.clone_depth
1362 else:
1363 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1364
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001365 need_to_fetch = not (optimized_fetch and
1366 (ID_RE.match(self.revisionExpr) and
1367 self._CheckForSha1()))
1368 if (need_to_fetch and
1369 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1370 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001371 no_tags=no_tags, prune=prune, depth=depth,
1372 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001373 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001374
1375 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001376 self._InitMRef()
1377 else:
1378 self._InitMirrorHead()
1379 try:
1380 os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
1381 except OSError:
1382 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001383 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001384
1385 def PostRepoUpgrade(self):
1386 self._InitHooks()
1387
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001388 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001389 if self.manifest.isGitcClient:
1390 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001391 for copyfile in self.copyfiles:
1392 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001393 for linkfile in self.linkfiles:
1394 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001395
Julien Camperguedd654222014-01-09 16:21:37 +01001396 def GetCommitRevisionId(self):
1397 """Get revisionId of a commit.
1398
1399 Use this method instead of GetRevisionId to get the id of the commit rather
1400 than the id of the current git object (for example, a tag)
1401
1402 """
1403 if not self.revisionExpr.startswith(R_TAGS):
1404 return self.GetRevisionId(self._allrefs)
1405
1406 try:
1407 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1408 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001409 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1410 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001411
David Pursehouse8a68ff92012-09-24 12:15:13 +09001412 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001413 if self.revisionId:
1414 return self.revisionId
1415
1416 rem = self.GetRemote(self.remote.name)
1417 rev = rem.ToLocal(self.revisionExpr)
1418
David Pursehouse8a68ff92012-09-24 12:15:13 +09001419 if all_refs is not None and rev in all_refs:
1420 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001421
1422 try:
1423 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1424 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001425 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1426 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001427
Martin Kellye4e94d22017-03-21 16:05:12 -07001428 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001429 """Perform only the local IO portion of the sync process.
1430 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001431 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001432 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001433 all_refs = self.bare_ref.all
1434 self.CleanPublishedCache(all_refs)
1435 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001436
David Pursehouse1d947b32012-10-25 12:23:11 +09001437 def _doff():
1438 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001439 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001440
Martin Kellye4e94d22017-03-21 16:05:12 -07001441 def _dosubmodules():
1442 self._SyncSubmodules(quiet=True)
1443
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001444 head = self.work_git.GetHead()
1445 if head.startswith(R_HEADS):
1446 branch = head[len(R_HEADS):]
1447 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001448 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001449 except KeyError:
1450 head = None
1451 else:
1452 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001453
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001454 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 # Currently on a detached HEAD. The user is assumed to
1456 # not have any local modifications worth worrying about.
1457 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001458 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001459 syncbuf.fail(self, _PriorSyncFailedError())
1460 return
1461
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001462 if head == revid:
1463 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001464 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001465 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001466 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001467 # The copy/linkfile config may have changed.
1468 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001469 return
1470 else:
1471 lost = self._revlist(not_rev(revid), HEAD)
1472 if lost:
1473 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001474
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001475 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001476 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001477 if submodules:
1478 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001479 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001480 syncbuf.fail(self, e)
1481 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001482 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001483 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001484
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001485 if head == revid:
1486 # No changes; don't do anything further.
1487 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001488 # The copy/linkfile config may have changed.
1489 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001490 return
1491
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001492 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001493
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001494 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001495 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001496 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001497 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001498 syncbuf.info(self,
1499 "leaving %s; does not track upstream",
1500 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001501 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001502 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001503 if submodules:
1504 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001505 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001506 syncbuf.fail(self, e)
1507 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001508 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001509 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001510
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001511 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001512 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001513 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001514 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515 if not_merged:
1516 if upstream_gain:
1517 # The user has published this branch and some of those
1518 # commits are not yet merged upstream. We do not want
1519 # to rewrite the published commits so we punt.
1520 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001521 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001522 "branch %s is published (but not merged) and is now "
1523 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001524 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001525 elif pub == head:
1526 # All published commits are merged, and thus we are a
1527 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001528 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001529 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001530 if submodules:
1531 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001532 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001533
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001534 # Examine the local commits not in the remote. Find the
1535 # last one attributed to this user, if any.
1536 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001537 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001538 last_mine = None
1539 cnt_mine = 0
1540 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301541 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001542 if committer_email == self.UserEmail:
1543 last_mine = commit_id
1544 cnt_mine += 1
1545
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001546 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001547 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001548
1549 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001550 syncbuf.fail(self, _DirtyError())
1551 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001552
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001553 # If the upstream switched on us, warn the user.
1554 #
1555 if branch.merge != self.revisionExpr:
1556 if branch.merge and self.revisionExpr:
1557 syncbuf.info(self,
1558 'manifest switched %s...%s',
1559 branch.merge,
1560 self.revisionExpr)
1561 elif branch.merge:
1562 syncbuf.info(self,
1563 'manifest no longer tracks %s',
1564 branch.merge)
1565
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001566 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001567 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001568 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001569 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001570 syncbuf.info(self,
1571 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001572 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001573
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001574 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001575 if not ID_RE.match(self.revisionExpr):
1576 # in case of manifest sync the revisionExpr might be a SHA1
1577 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001578 if not branch.merge.startswith('refs/'):
1579 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001580 branch.Save()
1581
Mike Pontillod3153822012-02-28 11:53:24 -08001582 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001583 def _docopyandlink():
1584 self._CopyAndLinkFiles()
1585
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001586 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001587 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001588 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001589 if submodules:
1590 syncbuf.later2(self, _dosubmodules)
1591 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001592 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001593 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001594 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001595 if submodules:
1596 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001597 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001598 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001599 syncbuf.fail(self, e)
1600 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001601 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001602 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001603 if submodules:
1604 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001605
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001606 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001607 # dest should already be an absolute path, but src is project relative
1608 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001609 abssrc = os.path.join(self.worktree, src)
1610 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001611
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001612 def AddLinkFile(self, src, dest, absdest):
1613 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001614 # make src relative path to dest
1615 absdestdir = os.path.dirname(absdest)
1616 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001617 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001618
James W. Mills24c13082012-04-12 15:04:13 -05001619 def AddAnnotation(self, name, value, keep):
1620 self.annotations.append(_Annotation(name, value, keep))
1621
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001622 def DownloadPatchSet(self, change_id, patch_id):
1623 """Download a single patch set of a single change to FETCH_HEAD.
1624 """
1625 remote = self.GetRemote(self.remote.name)
1626
1627 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001628 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001629 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001630 if GitCommand(self, cmd, bare=True).Wait() != 0:
1631 return None
1632 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001633 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001634 change_id,
1635 patch_id,
1636 self.bare_git.rev_parse('FETCH_HEAD'))
1637
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001638
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001639# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001640
Simran Basib9a1b732015-08-20 12:19:28 -07001641 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001642 """Create a new branch off the manifest's revision.
1643 """
Simran Basib9a1b732015-08-20 12:19:28 -07001644 if not branch_merge:
1645 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001646 head = self.work_git.GetHead()
1647 if head == (R_HEADS + name):
1648 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001649
David Pursehouse8a68ff92012-09-24 12:15:13 +09001650 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001651 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001652 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001653 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001654 capture_stdout=True,
1655 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001656
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001657 branch = self.GetBranch(name)
1658 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001659 branch.merge = branch_merge
1660 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1661 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001662 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001663
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001664 if head.startswith(R_HEADS):
1665 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001667 except KeyError:
1668 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001669 if revid and head and revid == head:
1670 ref = os.path.join(self.gitdir, R_HEADS + name)
1671 try:
1672 os.makedirs(os.path.dirname(ref))
1673 except OSError:
1674 pass
1675 _lwrite(ref, '%s\n' % revid)
1676 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1677 'ref: %s%s\n' % (R_HEADS, name))
1678 branch.Save()
1679 return True
1680
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001681 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001682 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001683 capture_stdout=True,
1684 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001685 branch.Save()
1686 return True
1687 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001688
Wink Saville02d79452009-04-10 13:01:24 -07001689 def CheckoutBranch(self, name):
1690 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001691
1692 Args:
1693 name: The name of the branch to checkout.
1694
1695 Returns:
1696 True if the checkout succeeded; False if it didn't; None if the branch
1697 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001698 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001699 rev = R_HEADS + name
1700 head = self.work_git.GetHead()
1701 if head == rev:
1702 # Already on the branch
1703 #
1704 return True
Wink Saville02d79452009-04-10 13:01:24 -07001705
David Pursehouse8a68ff92012-09-24 12:15:13 +09001706 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001707 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001708 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001709 except KeyError:
1710 # Branch does not exist in this project
1711 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001712 return None
Wink Saville02d79452009-04-10 13:01:24 -07001713
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001714 if head.startswith(R_HEADS):
1715 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001716 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001717 except KeyError:
1718 head = None
1719
1720 if head == revid:
1721 # Same revision; just update HEAD to point to the new
1722 # target branch, but otherwise take no other action.
1723 #
1724 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1725 'ref: %s%s\n' % (R_HEADS, name))
1726 return True
1727
1728 return GitCommand(self,
1729 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001730 capture_stdout=True,
1731 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001732
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001733 def AbandonBranch(self, name):
1734 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001735
1736 Args:
1737 name: The name of the branch to abandon.
1738
1739 Returns:
1740 True if the abandon succeeded; False if it didn't; None if the branch
1741 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001742 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001743 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001744 all_refs = self.bare_ref.all
1745 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001746 # Doesn't exist
1747 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001748
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001749 head = self.work_git.GetHead()
1750 if head == rev:
1751 # We can't destroy the branch while we are sitting
1752 # on it. Switch to a detached HEAD.
1753 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001754 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001755
David Pursehouse8a68ff92012-09-24 12:15:13 +09001756 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001757 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001758 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1759 '%s\n' % revid)
1760 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001761 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001762
1763 return GitCommand(self,
1764 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001765 capture_stdout=True,
1766 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001767
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001768 def PruneHeads(self):
1769 """Prune any topic branches already merged into upstream.
1770 """
1771 cb = self.CurrentBranch
1772 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001773 left = self._allrefs
1774 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001775 if name.startswith(R_HEADS):
1776 name = name[len(R_HEADS):]
1777 if cb is None or name != cb:
1778 kill.append(name)
1779
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001780 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001781 if cb is not None \
1782 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001783 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001784 self.work_git.DetachHead(HEAD)
1785 kill.append(cb)
1786
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001787 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001788 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001789
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001790 try:
1791 self.bare_git.DetachHead(rev)
1792
1793 b = ['branch', '-d']
1794 b.extend(kill)
1795 b = GitCommand(self, b, bare=True,
1796 capture_stdout=True,
1797 capture_stderr=True)
1798 b.Wait()
1799 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001800 if ID_RE.match(old):
1801 self.bare_git.DetachHead(old)
1802 else:
1803 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001804 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001805
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001806 for branch in kill:
1807 if (R_HEADS + branch) not in left:
1808 self.CleanPublishedCache()
1809 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001810
1811 if cb and cb not in kill:
1812 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001813 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001814
1815 kept = []
1816 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001817 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001818 branch = self.GetBranch(branch)
1819 base = branch.LocalMerge
1820 if not base:
1821 base = rev
1822 kept.append(ReviewableBranch(self, branch, base))
1823 return kept
1824
1825
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001826# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001827
1828 def GetRegisteredSubprojects(self):
1829 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001830
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001831 def rec(subprojects):
1832 if not subprojects:
1833 return
1834 result.extend(subprojects)
1835 for p in subprojects:
1836 rec(p.subprojects)
1837 rec(self.subprojects)
1838 return result
1839
1840 def _GetSubmodules(self):
1841 # Unfortunately we cannot call `git submodule status --recursive` here
1842 # because the working tree might not exist yet, and it cannot be used
1843 # without a working tree in its current implementation.
1844
1845 def get_submodules(gitdir, rev):
1846 # Parse .gitmodules for submodule sub_paths and sub_urls
1847 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1848 if not sub_paths:
1849 return []
1850 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1851 # revision of submodule repository
1852 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1853 submodules = []
1854 for sub_path, sub_url in zip(sub_paths, sub_urls):
1855 try:
1856 sub_rev = sub_revs[sub_path]
1857 except KeyError:
1858 # Ignore non-exist submodules
1859 continue
1860 submodules.append((sub_rev, sub_path, sub_url))
1861 return submodules
1862
1863 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1864 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001865
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001866 def parse_gitmodules(gitdir, rev):
1867 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1868 try:
Anthony King7bdac712014-07-16 12:56:40 +01001869 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1870 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001871 except GitError:
1872 return [], []
1873 if p.Wait() != 0:
1874 return [], []
1875
1876 gitmodules_lines = []
1877 fd, temp_gitmodules_path = tempfile.mkstemp()
1878 try:
1879 os.write(fd, p.stdout)
1880 os.close(fd)
1881 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001882 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1883 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001884 if p.Wait() != 0:
1885 return [], []
1886 gitmodules_lines = p.stdout.split('\n')
1887 except GitError:
1888 return [], []
1889 finally:
1890 os.remove(temp_gitmodules_path)
1891
1892 names = set()
1893 paths = {}
1894 urls = {}
1895 for line in gitmodules_lines:
1896 if not line:
1897 continue
1898 m = re_path.match(line)
1899 if m:
1900 names.add(m.group(1))
1901 paths[m.group(1)] = m.group(2)
1902 continue
1903 m = re_url.match(line)
1904 if m:
1905 names.add(m.group(1))
1906 urls[m.group(1)] = m.group(2)
1907 continue
1908 names = sorted(names)
1909 return ([paths.get(name, '') for name in names],
1910 [urls.get(name, '') for name in names])
1911
1912 def git_ls_tree(gitdir, rev, paths):
1913 cmd = ['ls-tree', rev, '--']
1914 cmd.extend(paths)
1915 try:
Anthony King7bdac712014-07-16 12:56:40 +01001916 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1917 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001918 except GitError:
1919 return []
1920 if p.Wait() != 0:
1921 return []
1922 objects = {}
1923 for line in p.stdout.split('\n'):
1924 if not line.strip():
1925 continue
1926 object_rev, object_path = line.split()[2:4]
1927 objects[object_path] = object_rev
1928 return objects
1929
1930 try:
1931 rev = self.GetRevisionId()
1932 except GitError:
1933 return []
1934 return get_submodules(self.gitdir, rev)
1935
1936 def GetDerivedSubprojects(self):
1937 result = []
1938 if not self.Exists:
1939 # If git repo does not exist yet, querying its submodules will
1940 # mess up its states; so return here.
1941 return result
1942 for rev, path, url in self._GetSubmodules():
1943 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001944 relpath, worktree, gitdir, objdir = \
1945 self.manifest.GetSubprojectPaths(self, name, path)
1946 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001947 if project:
1948 result.extend(project.GetDerivedSubprojects())
1949 continue
David James8d201162013-10-11 17:03:19 -07001950
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001951 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001952 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001953 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001954 review=self.remote.review,
1955 revision=self.remote.revision)
1956 subproject = Project(manifest=self.manifest,
1957 name=name,
1958 remote=remote,
1959 gitdir=gitdir,
1960 objdir=objdir,
1961 worktree=worktree,
1962 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001963 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001964 revisionId=rev,
1965 rebase=self.rebase,
1966 groups=self.groups,
1967 sync_c=self.sync_c,
1968 sync_s=self.sync_s,
1969 parent=self,
1970 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001971 result.append(subproject)
1972 result.extend(subproject.GetDerivedSubprojects())
1973 return result
1974
1975
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001976# Direct Git Commands ##
Chris AtLee2fb64662014-01-16 21:32:33 -05001977 def _CheckForSha1(self):
1978 try:
1979 # if revision (sha or tag) is not present then following function
1980 # throws an error.
1981 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1982 return True
1983 except GitError:
1984 # There is no such persistent revision. We have to fetch it.
1985 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001986
Julien Campergue335f5ef2013-10-16 11:02:35 +02001987 def _FetchArchive(self, tarpath, cwd=None):
1988 cmd = ['archive', '-v', '-o', tarpath]
1989 cmd.append('--remote=%s' % self.remote.url)
1990 cmd.append('--prefix=%s/' % self.relpath)
1991 cmd.append(self.revisionExpr)
1992
1993 command = GitCommand(self, cmd, cwd=cwd,
1994 capture_stdout=True,
1995 capture_stderr=True)
1996
1997 if command.Wait() != 0:
1998 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1999
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002000 def _RemoteFetch(self, name=None,
2001 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002002 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002003 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07002004 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09002005 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002006 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07002007 depth=None,
2008 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002009
2010 is_sha1 = False
2011 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002012 # The depth should not be used when fetching to a mirror because
2013 # it will result in a shallow repository that cannot be cloned or
2014 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002015 # The repo project should also never be synced with partial depth.
2016 if self.manifest.IsMirror or self.relpath == '.repo/repo':
2017 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09002018
Shawn Pearce69e04d82014-01-29 12:48:54 -08002019 if depth:
2020 current_branch_only = True
2021
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002022 if ID_RE.match(self.revisionExpr) is not None:
2023 is_sha1 = True
2024
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002025 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002026 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002027 # this is a tag and its sha1 value should never change
2028 tag_name = self.revisionExpr[len(R_TAGS):]
2029
2030 if is_sha1 or tag_name is not None:
Chris AtLee2fb64662014-01-16 21:32:33 -05002031 if self._CheckForSha1():
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002032 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002033 if is_sha1 and not depth:
2034 # When syncing a specific commit and --depth is not set:
2035 # * if upstream is explicitly specified and is not a sha1, fetch only
2036 # upstream as users expect only upstream to be fetch.
2037 # Note: The commit might not be in upstream in which case the sync
2038 # will fail.
2039 # * otherwise, fetch all branches to make sure we end up with the
2040 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002041 if self.upstream:
2042 current_branch_only = not ID_RE.match(self.upstream)
2043 else:
2044 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002045
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002046 if not name:
2047 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002048
2049 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002050 remote = self.GetRemote(name)
2051 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002052 ssh_proxy = True
2053
Shawn O. Pearce88443382010-10-08 10:02:09 +02002054 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002055 if alt_dir and 'objects' == os.path.basename(alt_dir):
2056 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002057 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2058 remote = self.GetRemote(name)
2059
David Pursehouse8a68ff92012-09-24 12:15:13 +09002060 all_refs = self.bare_ref.all
2061 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002062 tmp = set()
2063
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302064 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002065 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002066 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002067 all_refs[r] = ref_id
2068 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002069 continue
2070
David Pursehouse8a68ff92012-09-24 12:15:13 +09002071 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002072 continue
2073
David Pursehouse8a68ff92012-09-24 12:15:13 +09002074 r = 'refs/_alt/%s' % ref_id
2075 all_refs[r] = ref_id
2076 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002077 tmp.add(r)
2078
heping3d7bbc92017-04-12 19:51:47 +08002079 tmp_packed_lines = []
2080 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002081
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302082 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002083 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002084 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002085 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002086 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002087
heping3d7bbc92017-04-12 19:51:47 +08002088 tmp_packed = ''.join(tmp_packed_lines)
2089 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002090 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002091 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002092 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002093
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002094 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002095
Conley Owensf97e8382015-01-21 11:12:46 -08002096 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002097 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002098 else:
2099 # If this repo has shallow objects, then we don't know which refs have
2100 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2101 # do this with projects that don't have shallow objects, since it is less
2102 # efficient.
2103 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2104 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002105
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002106 if quiet:
2107 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002108 if not self.worktree:
2109 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002110 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002111
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002112 # If using depth then we should not get all the tags since they may
2113 # be outside of the depth.
2114 if no_tags or depth:
2115 cmd.append('--no-tags')
2116 else:
2117 cmd.append('--tags')
2118
David Pursehouse74cfd272015-10-14 10:50:15 +09002119 if prune:
2120 cmd.append('--prune')
2121
Martin Kellye4e94d22017-03-21 16:05:12 -07002122 if submodules:
2123 cmd.append('--recurse-submodules=on-demand')
2124
Conley Owens80b87fe2014-05-09 17:13:44 -07002125 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002126 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002127 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002128 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002129 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002130 spec.append('tag')
2131 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002132
David Pursehouse403b64e2015-04-27 10:41:33 +09002133 if not self.manifest.IsMirror:
2134 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002135 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002136 # Shallow checkout of a specific commit, fetch from that commit and not
2137 # the heads only as the commit might be deeper in the history.
2138 spec.append(branch)
2139 else:
2140 if is_sha1:
2141 branch = self.upstream
2142 if branch is not None and branch.strip():
2143 if not branch.startswith('refs/'):
2144 branch = R_HEADS + branch
2145 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002146 cmd.extend(spec)
2147
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002148 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002149 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002150 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002151 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002152 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002153 ok = True
2154 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002155 # If needed, run the 'git remote prune' the first time through the loop
2156 elif (not _i and
2157 "error:" in gitcmd.stderr and
2158 "git remote prune" in gitcmd.stderr):
2159 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002160 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002161 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002162 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002163 break
2164 continue
Brian Harring14a66742012-09-28 20:21:57 -07002165 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002166 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2167 # in sha1 mode, we just tried sync'ing from the upstream field; it
2168 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002169 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002170 elif ret < 0:
2171 # Git died with a signal, exit immediately
2172 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002173 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002174
2175 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002176 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002177 if old_packed != '':
2178 _lwrite(packed_refs, old_packed)
2179 else:
2180 os.remove(packed_refs)
2181 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002182
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002183 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002184 # We just synced the upstream given branch; verify we
2185 # got what we wanted, else trigger a second run of all
2186 # refs.
Chris AtLee2fb64662014-01-16 21:32:33 -05002187 if not self._CheckForSha1():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002188 if current_branch_only and depth:
2189 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002190 return self._RemoteFetch(name=name,
2191 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002192 initial=False, quiet=quiet, alt_dir=alt_dir,
2193 depth=None)
2194 else:
2195 # Avoid infinite recursion: sync all branches with depth set to None
2196 return self._RemoteFetch(name=name, current_branch_only=False,
2197 initial=False, quiet=quiet, alt_dir=alt_dir,
2198 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002199
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002200 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002201
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002202 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002203 if initial and \
2204 (self.manifest.manifestProject.config.GetString('repo.depth') or
2205 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002206 return False
2207
2208 remote = self.GetRemote(self.remote.name)
2209 bundle_url = remote.url + '/clone.bundle'
2210 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002211 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2212 'persistent-http',
2213 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002214 return False
2215
2216 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2217 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2218
2219 exist_dst = os.path.exists(bundle_dst)
2220 exist_tmp = os.path.exists(bundle_tmp)
2221
2222 if not initial and not exist_dst and not exist_tmp:
2223 return False
2224
2225 if not exist_dst:
2226 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2227 if not exist_dst:
2228 return False
2229
2230 cmd = ['fetch']
2231 if quiet:
2232 cmd.append('--quiet')
2233 if not self.worktree:
2234 cmd.append('--update-head-ok')
2235 cmd.append(bundle_dst)
2236 for f in remote.fetch:
2237 cmd.append(str(f))
2238 cmd.append('refs/tags/*:refs/tags/*')
2239
2240 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002241 if os.path.exists(bundle_dst):
2242 os.remove(bundle_dst)
2243 if os.path.exists(bundle_tmp):
2244 os.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002245 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002246
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002247 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002248 if os.path.exists(dstPath):
2249 os.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002251 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002252 if quiet:
2253 cmd += ['--silent']
2254 if os.path.exists(tmpPath):
2255 size = os.stat(tmpPath).st_size
2256 if size >= 1024:
2257 cmd += ['--continue-at', '%d' % (size,)]
2258 else:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002259 os.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002260 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2261 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002262 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002263 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002264 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002265 if srcUrl.startswith('persistent-'):
2266 srcUrl = srcUrl[len('persistent-'):]
2267 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002268
Dave Borowitz137d0132015-01-02 11:12:54 -08002269 if IsTrace():
2270 Trace('%s', ' '.join(cmd))
2271 try:
2272 proc = subprocess.Popen(cmd)
2273 except OSError:
2274 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002275
Dave Borowitz137d0132015-01-02 11:12:54 -08002276 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002277
Dave Borowitz137d0132015-01-02 11:12:54 -08002278 if curlret == 22:
2279 # From curl man page:
2280 # 22: HTTP page not retrieved. The requested url was not found or
2281 # returned another error with the HTTP error code being 400 or above.
2282 # This return code only appears if -f, --fail is used.
2283 if not quiet:
2284 print("Server does not provide clone.bundle; ignoring.",
2285 file=sys.stderr)
2286 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002287
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002288 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002289 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002290 os.rename(tmpPath, dstPath)
2291 return True
2292 else:
2293 os.remove(tmpPath)
2294 return False
2295 else:
2296 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002297
Kris Giesingc8d882a2014-12-23 13:02:32 -08002298 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002299 try:
2300 with open(path) as f:
2301 if f.read(16) == '# v2 git bundle\n':
2302 return True
2303 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002304 if not quiet:
2305 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002306 return False
2307 except OSError:
2308 return False
2309
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002310 def _Checkout(self, rev, quiet=False):
2311 cmd = ['checkout']
2312 if quiet:
2313 cmd.append('-q')
2314 cmd.append(rev)
2315 cmd.append('--')
2316 if GitCommand(self, cmd).Wait() != 0:
2317 if self._allrefs:
2318 raise GitError('%s checkout %s ' % (self.name, rev))
2319
Anthony King7bdac712014-07-16 12:56:40 +01002320 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002321 cmd = ['cherry-pick']
2322 cmd.append(rev)
2323 cmd.append('--')
2324 if GitCommand(self, cmd).Wait() != 0:
2325 if self._allrefs:
2326 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2327
Anthony King7bdac712014-07-16 12:56:40 +01002328 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002329 cmd = ['revert']
2330 cmd.append('--no-edit')
2331 cmd.append(rev)
2332 cmd.append('--')
2333 if GitCommand(self, cmd).Wait() != 0:
2334 if self._allrefs:
2335 raise GitError('%s revert %s ' % (self.name, rev))
2336
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002337 def _ResetHard(self, rev, quiet=True):
2338 cmd = ['reset', '--hard']
2339 if quiet:
2340 cmd.append('-q')
2341 cmd.append(rev)
2342 if GitCommand(self, cmd).Wait() != 0:
2343 raise GitError('%s reset --hard %s ' % (self.name, rev))
2344
Martin Kellye4e94d22017-03-21 16:05:12 -07002345 def _SyncSubmodules(self, quiet=True):
2346 cmd = ['submodule', 'update', '--init', '--recursive']
2347 if quiet:
2348 cmd.append('-q')
2349 if GitCommand(self, cmd).Wait() != 0:
2350 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2351
Anthony King7bdac712014-07-16 12:56:40 +01002352 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002353 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002354 if onto is not None:
2355 cmd.extend(['--onto', onto])
2356 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002357 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002358 raise GitError('%s rebase %s ' % (self.name, upstream))
2359
Pierre Tardy3d125942012-05-04 12:18:12 +02002360 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002361 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002362 if ffonly:
2363 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002364 if GitCommand(self, cmd).Wait() != 0:
2365 raise GitError('%s merge %s ' % (self.name, head))
2366
Kevin Degiabaa7f32014-11-12 11:27:45 -07002367 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002368 init_git_dir = not os.path.exists(self.gitdir)
2369 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002370 try:
2371 # Initialize the bare repository, which contains all of the objects.
2372 if init_obj_dir:
2373 os.makedirs(self.objdir)
2374 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002375
Kevin Degib1a07b82015-07-27 13:33:43 -06002376 # If we have a separate directory to hold refs, initialize it as well.
2377 if self.objdir != self.gitdir:
2378 if init_git_dir:
2379 os.makedirs(self.gitdir)
2380
2381 if init_obj_dir or init_git_dir:
2382 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2383 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002384 try:
2385 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2386 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002387 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002388 print("Retrying clone after deleting %s" %
2389 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002390 try:
2391 shutil.rmtree(os.path.realpath(self.gitdir))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002392 if self.worktree and os.path.exists(os.path.realpath
2393 (self.worktree)):
Kevin Degiabaa7f32014-11-12 11:27:45 -07002394 shutil.rmtree(os.path.realpath(self.worktree))
2395 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2396 except:
2397 raise e
2398 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002399
Kevin Degi384b3c52014-10-16 16:02:58 -06002400 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002401 mp = self.manifest.manifestProject
2402 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002403
Kevin Degib1a07b82015-07-27 13:33:43 -06002404 if ref_dir or mirror_git:
2405 if not mirror_git:
2406 mirror_git = os.path.join(ref_dir, self.name + '.git')
2407 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2408 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002409
Kevin Degib1a07b82015-07-27 13:33:43 -06002410 if os.path.exists(mirror_git):
2411 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002412
Kevin Degib1a07b82015-07-27 13:33:43 -06002413 elif os.path.exists(repo_git):
2414 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002415
Kevin Degib1a07b82015-07-27 13:33:43 -06002416 else:
2417 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002418
Kevin Degib1a07b82015-07-27 13:33:43 -06002419 if ref_dir:
2420 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2421 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002422
Kevin Degib1a07b82015-07-27 13:33:43 -06002423 self._UpdateHooks()
2424
2425 m = self.manifest.manifestProject.config
2426 for key in ['user.name', 'user.email']:
2427 if m.Has(key, include_defaults=False):
2428 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002429 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002430 if self.manifest.IsMirror:
2431 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002432 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002433 self.config.SetString('core.bare', None)
2434 except Exception:
2435 if init_obj_dir and os.path.exists(self.objdir):
2436 shutil.rmtree(self.objdir)
2437 if init_git_dir and os.path.exists(self.gitdir):
2438 shutil.rmtree(self.gitdir)
2439 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002440
Jimmie Westera0444582012-10-24 13:44:42 +02002441 def _UpdateHooks(self):
2442 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002443 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002444
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002445 def _InitHooks(self):
Jesse Hall672cc492013-11-27 11:17:13 -08002446 hooks = os.path.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002447 if not os.path.exists(hooks):
2448 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002449 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002450 name = os.path.basename(stock_hook)
2451
Victor Boivie65e0f352011-04-18 11:23:29 +02002452 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002453 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002454 # Don't install a Gerrit Code Review hook if this
2455 # project does not appear to use it for reviews.
2456 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002457 # Since the manifest project is one of those, but also
2458 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002459 continue
2460
2461 dst = os.path.join(hooks, name)
2462 if os.path.islink(dst):
2463 continue
2464 if os.path.exists(dst):
2465 if filecmp.cmp(stock_hook, dst, shallow=False):
2466 os.remove(dst)
2467 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002468 _warn("%s: Not replacing locally modified %s hook",
2469 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002470 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002471 try:
Mickaël Salaünb9477bc2012-08-05 13:39:26 +02002472 os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002473 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002474 if e.errno == errno.EPERM:
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002475 raise GitError('filesystem must support symlinks')
2476 else:
2477 raise
2478
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002479 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002480 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002481 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002482 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002483 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002484 remote.review = self.remote.review
2485 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002486
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002487 if self.worktree:
2488 remote.ResetFetch(mirror=False)
2489 else:
2490 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002491 remote.Save()
2492
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002493 def _InitMRef(self):
2494 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002495 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002496
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002497 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002498 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002499
2500 def _InitAnyMRef(self, ref):
2501 cur = self.bare_ref.symref(ref)
2502
2503 if self.revisionId:
2504 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2505 msg = 'manifest set to %s' % self.revisionId
2506 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002507 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002508 else:
2509 remote = self.GetRemote(self.remote.name)
2510 dst = remote.ToLocal(self.revisionExpr)
2511 if cur != dst:
2512 msg = 'manifest set to %s' % self.revisionExpr
2513 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002514
Kevin Degi384b3c52014-10-16 16:02:58 -06002515 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002516 symlink_files = self.shareable_files[:]
2517 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002518 if share_refs:
2519 symlink_files += self.working_tree_files
2520 symlink_dirs += self.working_tree_dirs
2521 to_symlink = symlink_files + symlink_dirs
2522 for name in set(to_symlink):
2523 dst = os.path.realpath(os.path.join(destdir, name))
2524 if os.path.lexists(dst):
2525 src = os.path.realpath(os.path.join(srcdir, name))
2526 # Fail if the links are pointing to the wrong place
2527 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002528 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002529 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002530 'work tree. If you\'re comfortable with the '
2531 'possibility of losing the work tree\'s git metadata,'
2532 ' use `repo sync --force-sync {0}` to '
2533 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002534
David James8d201162013-10-11 17:03:19 -07002535 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2536 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2537
2538 Args:
2539 gitdir: The bare git repository. Must already be initialized.
2540 dotgit: The repository you would like to initialize.
2541 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2542 Only one work tree can store refs under a given |gitdir|.
2543 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2544 This saves you the effort of initializing |dotgit| yourself.
2545 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002546 symlink_files = self.shareable_files[:]
2547 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002548 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002549 symlink_files += self.working_tree_files
2550 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002551 to_symlink = symlink_files + symlink_dirs
2552
2553 to_copy = []
2554 if copy_all:
2555 to_copy = os.listdir(gitdir)
2556
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002557 dotgit = os.path.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002558 for name in set(to_copy).union(to_symlink):
2559 try:
2560 src = os.path.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002561 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002562
Kevin Degi384b3c52014-10-16 16:02:58 -06002563 if os.path.lexists(dst):
2564 continue
David James8d201162013-10-11 17:03:19 -07002565
2566 # If the source dir doesn't exist, create an empty dir.
2567 if name in symlink_dirs and not os.path.lexists(src):
2568 os.makedirs(src)
2569
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002570 if name in to_symlink:
2571 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
2572 elif copy_all and not os.path.islink(dst):
2573 if os.path.isdir(src):
2574 shutil.copytree(src, dst)
2575 elif os.path.isfile(src):
2576 shutil.copy(src, dst)
2577
Conley Owens80b87fe2014-05-09 17:13:44 -07002578 # If the source file doesn't exist, ensure the destination
2579 # file doesn't either.
2580 if name in symlink_files and not os.path.lexists(src):
2581 try:
2582 os.remove(dst)
2583 except OSError:
2584 pass
2585
David James8d201162013-10-11 17:03:19 -07002586 except OSError as e:
2587 if e.errno == errno.EPERM:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002588 raise DownloadError('filesystem must support symlinks')
David James8d201162013-10-11 17:03:19 -07002589 else:
2590 raise
2591
Martin Kellye4e94d22017-03-21 16:05:12 -07002592 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002593 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002594 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002595 try:
2596 if init_dotgit:
2597 os.makedirs(dotgit)
2598 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2599 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002600
Kevin Degiabaa7f32014-11-12 11:27:45 -07002601 try:
2602 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2603 except GitError as e:
2604 if force_sync:
2605 try:
2606 shutil.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002607 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002608 except:
2609 raise e
2610 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002611
Kevin Degib1a07b82015-07-27 13:33:43 -06002612 if init_dotgit:
2613 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002614
Kevin Degib1a07b82015-07-27 13:33:43 -06002615 cmd = ['read-tree', '--reset', '-u']
2616 cmd.append('-v')
2617 cmd.append(HEAD)
2618 if GitCommand(self, cmd).Wait() != 0:
2619 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002620
Martin Kellye4e94d22017-03-21 16:05:12 -07002621 if submodules:
2622 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002623 self._CopyAndLinkFiles()
2624 except Exception:
2625 if init_dotgit:
2626 shutil.rmtree(dotgit)
2627 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002628
2629 def _gitdir_path(self, path):
David James8d201162013-10-11 17:03:19 -07002630 return os.path.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002631
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002632 def _revlist(self, *args, **kw):
2633 a = []
2634 a.extend(args)
2635 a.append('--')
2636 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002637
2638 @property
2639 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002640 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002641
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002642 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002643 """Get logs between two revisions of this project."""
2644 comp = '..'
2645 if rev1:
2646 revs = [rev1]
2647 if rev2:
2648 revs.extend([comp, rev2])
2649 cmd = ['log', ''.join(revs)]
2650 out = DiffColoring(self.config)
2651 if out.is_on and color:
2652 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002653 if pretty_format is not None:
2654 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002655 if oneline:
2656 cmd.append('--oneline')
2657
2658 try:
2659 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2660 if log.Wait() == 0:
2661 return log.stdout
2662 except GitError:
2663 # worktree may not exist if groups changed for example. In that case,
2664 # try in gitdir instead.
2665 if not os.path.exists(self.worktree):
2666 return self.bare_git.log(*cmd[1:])
2667 else:
2668 raise
2669 return None
2670
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002671 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2672 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002673 """Get the list of logs from this revision to given revisionId"""
2674 logs = {}
2675 selfId = self.GetRevisionId(self._allrefs)
2676 toId = toProject.GetRevisionId(toProject._allrefs)
2677
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002678 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2679 pretty_format=pretty_format)
2680 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2681 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002682 return logs
2683
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002684 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002685
David James8d201162013-10-11 17:03:19 -07002686 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002687 self._project = project
2688 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002689 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002690
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002691 def LsOthers(self):
2692 p = GitCommand(self._project,
2693 ['ls-files',
2694 '-z',
2695 '--others',
2696 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002697 bare=False,
David James8d201162013-10-11 17:03:19 -07002698 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002699 capture_stdout=True,
2700 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002701 if p.Wait() == 0:
2702 out = p.stdout
2703 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002704 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002705 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002706 return []
2707
2708 def DiffZ(self, name, *args):
2709 cmd = [name]
2710 cmd.append('-z')
2711 cmd.extend(args)
2712 p = GitCommand(self._project,
2713 cmd,
David James8d201162013-10-11 17:03:19 -07002714 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002715 bare=False,
2716 capture_stdout=True,
2717 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002718 try:
2719 out = p.process.stdout.read()
2720 r = {}
2721 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002722 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002723 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002724 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002725 info = next(out)
2726 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002727 except StopIteration:
2728 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002729
2730 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002731
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002732 def __init__(self, path, omode, nmode, oid, nid, state):
2733 self.path = path
2734 self.src_path = None
2735 self.old_mode = omode
2736 self.new_mode = nmode
2737 self.old_id = oid
2738 self.new_id = nid
2739
2740 if len(state) == 1:
2741 self.status = state
2742 self.level = None
2743 else:
2744 self.status = state[:1]
2745 self.level = state[1:]
2746 while self.level.startswith('0'):
2747 self.level = self.level[1:]
2748
2749 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002750 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002751 if info.status in ('R', 'C'):
2752 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002753 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002754 r[info.path] = info
2755 return r
2756 finally:
2757 p.Wait()
2758
2759 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002760 if self._bare:
2761 path = os.path.join(self._project.gitdir, HEAD)
2762 else:
2763 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002764 try:
2765 fd = open(path, 'rb')
Dan Sandler53e902a2014-03-09 13:20:02 -04002766 except IOError as e:
2767 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002768 try:
2769 line = fd.read()
2770 finally:
2771 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302772 try:
2773 line = line.decode()
2774 except AttributeError:
2775 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002776 if line.startswith('ref: '):
2777 return line[5:-1]
2778 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779
2780 def SetHead(self, ref, message=None):
2781 cmdv = []
2782 if message is not None:
2783 cmdv.extend(['-m', message])
2784 cmdv.append(HEAD)
2785 cmdv.append(ref)
2786 self.symbolic_ref(*cmdv)
2787
2788 def DetachHead(self, new, message=None):
2789 cmdv = ['--no-deref']
2790 if message is not None:
2791 cmdv.extend(['-m', message])
2792 cmdv.append(HEAD)
2793 cmdv.append(new)
2794 self.update_ref(*cmdv)
2795
2796 def UpdateRef(self, name, new, old=None,
2797 message=None,
2798 detach=False):
2799 cmdv = []
2800 if message is not None:
2801 cmdv.extend(['-m', message])
2802 if detach:
2803 cmdv.append('--no-deref')
2804 cmdv.append(name)
2805 cmdv.append(new)
2806 if old is not None:
2807 cmdv.append(old)
2808 self.update_ref(*cmdv)
2809
2810 def DeleteRef(self, name, old=None):
2811 if not old:
2812 old = self.rev_parse(name)
2813 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002814 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002815
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002816 def rev_list(self, *args, **kw):
2817 if 'format' in kw:
2818 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2819 else:
2820 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002821 cmdv.extend(args)
2822 p = GitCommand(self._project,
2823 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002824 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002825 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002826 capture_stdout=True,
2827 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828 r = []
2829 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002830 if line[-1] == '\n':
2831 line = line[:-1]
2832 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002833 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002834 raise GitError('%s rev-list %s: %s' %
2835 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002836 return r
2837
2838 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002839 """Allow arbitrary git commands using pythonic syntax.
2840
2841 This allows you to do things like:
2842 git_obj.rev_parse('HEAD')
2843
2844 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2845 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002846 Any other positional arguments will be passed to the git command, and the
2847 following keyword arguments are supported:
2848 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002849
2850 Args:
2851 name: The name of the git command to call. Any '_' characters will
2852 be replaced with '-'.
2853
2854 Returns:
2855 A callable object that will try to call git with the named command.
2856 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002858
Dave Borowitz091f8932012-10-23 17:01:04 -07002859 def runner(*args, **kwargs):
2860 cmdv = []
2861 config = kwargs.pop('config', None)
2862 for k in kwargs:
2863 raise TypeError('%s() got an unexpected keyword argument %r'
2864 % (name, k))
2865 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002866 if not git_require((1, 7, 2)):
2867 raise ValueError('cannot set config on command line for %s()'
2868 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302869 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002870 cmdv.append('-c')
2871 cmdv.append('%s=%s' % (k, v))
2872 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002873 cmdv.extend(args)
2874 p = GitCommand(self._project,
2875 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002876 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002877 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002878 capture_stdout=True,
2879 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002880 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002881 raise GitError('%s %s: %s' %
2882 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002883 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302884 try:
Conley Owensedd01512013-09-26 12:59:58 -07002885 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302886 except AttributeError:
2887 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002888 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2889 return r[:-1]
2890 return r
2891 return runner
2892
2893
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002894class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002895
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002896 def __str__(self):
2897 return 'prior sync failed; rebase still in progress'
2898
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002899
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002900class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002901
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002902 def __str__(self):
2903 return 'contains uncommitted changes'
2904
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002905
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002906class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002907
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002908 def __init__(self, project, text):
2909 self.project = project
2910 self.text = text
2911
2912 def Print(self, syncbuf):
2913 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2914 syncbuf.out.nl()
2915
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002916
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002917class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002918
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002919 def __init__(self, project, why):
2920 self.project = project
2921 self.why = why
2922
2923 def Print(self, syncbuf):
2924 syncbuf.out.fail('error: %s/: %s',
2925 self.project.relpath,
2926 str(self.why))
2927 syncbuf.out.nl()
2928
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002929
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002930class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002931
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002932 def __init__(self, project, action):
2933 self.project = project
2934 self.action = action
2935
2936 def Run(self, syncbuf):
2937 out = syncbuf.out
2938 out.project('project %s/', self.project.relpath)
2939 out.nl()
2940 try:
2941 self.action()
2942 out.nl()
2943 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002944 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002945 out.nl()
2946 return False
2947
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002948
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002949class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002950
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002951 def __init__(self, config):
2952 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002953 self.project = self.printer('header', attr='bold')
2954 self.info = self.printer('info')
2955 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002956
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002957
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002958class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002959
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002960 def __init__(self, config, detach_head=False):
2961 self._messages = []
2962 self._failures = []
2963 self._later_queue1 = []
2964 self._later_queue2 = []
2965
2966 self.out = _SyncColoring(config)
2967 self.out.redirect(sys.stderr)
2968
2969 self.detach_head = detach_head
2970 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002971 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002972
2973 def info(self, project, fmt, *args):
2974 self._messages.append(_InfoMessage(project, fmt % args))
2975
2976 def fail(self, project, err=None):
2977 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002978 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002979
2980 def later1(self, project, what):
2981 self._later_queue1.append(_Later(project, what))
2982
2983 def later2(self, project, what):
2984 self._later_queue2.append(_Later(project, what))
2985
2986 def Finish(self):
2987 self._PrintMessages()
2988 self._RunLater()
2989 self._PrintMessages()
2990 return self.clean
2991
David Rileye0684ad2017-04-05 00:02:59 -07002992 def Recently(self):
2993 recent_clean = self.recent_clean
2994 self.recent_clean = True
2995 return recent_clean
2996
2997 def _MarkUnclean(self):
2998 self.clean = False
2999 self.recent_clean = False
3000
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003001 def _RunLater(self):
3002 for q in ['_later_queue1', '_later_queue2']:
3003 if not self._RunQueue(q):
3004 return
3005
3006 def _RunQueue(self, queue):
3007 for m in getattr(self, queue):
3008 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07003009 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07003010 return False
3011 setattr(self, queue, [])
3012 return True
3013
3014 def _PrintMessages(self):
3015 for m in self._messages:
3016 m.Print(self)
3017 for m in self._failures:
3018 m.Print(self)
3019
3020 self._messages = []
3021 self._failures = []
3022
3023
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003024class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003025
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003026 """A special project housed under .repo.
3027 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003028
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003029 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003030 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003031 manifest=manifest,
3032 name=name,
3033 gitdir=gitdir,
3034 objdir=gitdir,
3035 worktree=worktree,
3036 remote=RemoteSpec('origin'),
3037 relpath='.repo/%s' % name,
3038 revisionExpr='refs/heads/master',
3039 revisionId=None,
3040 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003041
3042 def PreSync(self):
3043 if self.Exists:
3044 cb = self.CurrentBranch
3045 if cb:
3046 base = self.GetBranch(cb).merge
3047 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003048 self.revisionExpr = base
3049 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003050
Martin Kelly224a31a2017-07-10 14:46:25 -07003051 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003052 """ Prepare MetaProject for manifest branch switch
3053 """
3054
3055 # detach and delete manifest branch, allowing a new
3056 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003057 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003058 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003059 syncbuf.Finish()
3060
3061 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003062 ['update-ref', '-d', 'refs/heads/default'],
3063 capture_stdout=True,
3064 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003065
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003066 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003067 def LastFetch(self):
3068 try:
3069 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3070 return os.path.getmtime(fh)
3071 except OSError:
3072 return 0
3073
3074 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003075 def HasChanges(self):
3076 """Has the remote received new commits not yet checked out?
3077 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003078 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003079 return False
3080
David Pursehouse8a68ff92012-09-24 12:15:13 +09003081 all_refs = self.bare_ref.all
3082 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003083 head = self.work_git.GetHead()
3084 if head.startswith(R_HEADS):
3085 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003086 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003087 except KeyError:
3088 head = None
3089
3090 if revid == head:
3091 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003092 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003093 return True
3094 return False