blob: 3ba8a0738056adb2f9f15b8e1960f9430a4cd2be [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080036from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080037from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070038import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070039from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Shawn O. Pearced237b692009-04-17 18:49:50 -070041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
David Pursehouse59bbb582013-05-17 10:49:33 +090043from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040044if is_python3():
45 import urllib.parse
46else:
47 import imp
48 import urlparse
49 urllib = imp.new_module('urllib')
50 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090051 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090053 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070066 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080068 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070069 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700106 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700107 d = os.path.join(d, 'hooks')
108 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000179 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200180 private=False,
181 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200182 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800183 validate_certs=True,
184 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800185 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700186 people,
Brian Harring435370c2012-07-28 15:37:04 -0700187 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000188 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200189 private=private,
190 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200191 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800192 validate_certs=validate_certs,
193 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700194
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700195 def GetPublishedRefs(self):
196 refs = {}
197 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700198 self.branch.remote.SshReviewUrl(self.project.UserEmail),
199 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700200 for line in output.split('\n'):
201 try:
202 (sha, ref) = line.split()
203 refs[sha] = ref
204 except ValueError:
205 pass
206
207 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700209
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700211
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700212 def __init__(self, config):
213 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100214 self.project = self.printer('header', attr='bold')
215 self.branch = self.printer('header', attr='bold')
216 self.nobranch = self.printer('nobranch', fg='red')
217 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
Anthony King7bdac712014-07-16 12:56:40 +0100219 self.added = self.printer('added', fg='green')
220 self.changed = self.printer('changed', fg='red')
221 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
223
224class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700225
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700226 def __init__(self, config):
227 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100228 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700229
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700230
Anthony King7bdac712014-07-16 12:56:40 +0100231class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700232
James W. Mills24c13082012-04-12 15:04:13 -0500233 def __init__(self, name, value, keep):
234 self.name = name
235 self.value = value
236 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700238
Anthony King7bdac712014-07-16 12:56:40 +0100239class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700240
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800241 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242 self.src = src
243 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 self.abs_src = abssrc
245 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246
247 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800248 src = self.abs_src
249 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700250 # copy file if it does not exist or is out of date
251 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
252 try:
253 # remove existing file first, since it might be read-only
254 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800255 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400256 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200257 dest_dir = os.path.dirname(dest)
258 if not os.path.isdir(dest_dir):
259 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260 shutil.copy(src, dest)
261 # make the file read-only
262 mode = os.stat(dest)[stat.ST_MODE]
263 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
264 os.chmod(dest, mode)
265 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700266 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700267
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700268
Anthony King7bdac712014-07-16 12:56:40 +0100269class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700270
Wink Saville4c426ef2015-06-03 08:05:17 -0700271 def __init__(self, git_worktree, src, dest, relsrc, absdest):
272 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500273 self.src = src
274 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700275 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 self.abs_dest = absdest
277
Wink Saville4c426ef2015-06-03 08:05:17 -0700278 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700280 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 try:
282 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800283 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800284 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700286 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500287 if not os.path.isdir(dest_dir):
288 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700289 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500290 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700291 _error('Cannot link file %s to %s', relSrc, absDest)
292
293 def _Link(self):
294 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
295 on the src linking all of the files in the source in to the destination
296 directory.
297 """
298 # We use the absSrc to handle the situation where the current directory
299 # is not the root of the repo
300 absSrc = os.path.join(self.git_worktree, self.src)
301 if os.path.exists(absSrc):
302 # Entity exists so just a simple one to one link operation
303 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
304 else:
305 # Entity doesn't exist assume there is a wild card
306 absDestDir = self.abs_dest
307 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
308 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700309 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700310 else:
311 absSrcFiles = glob.glob(absSrc)
312 for absSrcFile in absSrcFiles:
313 # Create a releative path from source dir to destination dir
314 absSrcDir = os.path.dirname(absSrcFile)
315 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
316
317 # Get the source file name
318 srcFile = os.path.basename(absSrcFile)
319
320 # Now form the final full paths to srcFile. They will be
321 # absolute for the desintaiton and relative for the srouce.
322 absDest = os.path.join(absDestDir, srcFile)
323 relSrc = os.path.join(relSrcDir, srcFile)
324 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500325
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700326
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700328
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700329 def __init__(self,
330 name,
Anthony King7bdac712014-07-16 12:56:40 +0100331 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700332 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100333 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700334 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700335 orig_name=None,
336 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700337 self.name = name
338 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700339 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700340 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100341 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700342 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700343 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700344
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700345
Doug Anderson37282b42011-03-04 11:54:18 -0800346class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700347
Doug Anderson37282b42011-03-04 11:54:18 -0800348 """A RepoHook contains information about a script to run as a hook.
349
350 Hooks are used to run a python script before running an upload (for instance,
351 to run presubmit checks). Eventually, we may have hooks for other actions.
352
353 This shouldn't be confused with files in the 'repo/hooks' directory. Those
354 files are copied into each '.git/hooks' folder for each project. Repo-level
355 hooks are associated instead with repo actions.
356
357 Hooks are always python. When a hook is run, we will load the hook into the
358 interpreter and execute its main() function.
359 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700360
Doug Anderson37282b42011-03-04 11:54:18 -0800361 def __init__(self,
362 hook_type,
363 hooks_project,
364 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400365 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800366 abort_if_user_denies=False):
367 """RepoHook constructor.
368
369 Params:
370 hook_type: A string representing the type of hook. This is also used
371 to figure out the name of the file containing the hook. For
372 example: 'pre-upload'.
373 hooks_project: The project containing the repo hooks. If you have a
374 manifest, this is manifest.repo_hooks_project. OK if this is None,
375 which will make the hook a no-op.
376 topdir: Repo's top directory (the one containing the .repo directory).
377 Scripts will run with CWD as this directory. If you have a manifest,
378 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400379 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800380 abort_if_user_denies: If True, we'll throw a HookError() if the user
381 doesn't allow us to run the hook.
382 """
383 self._hook_type = hook_type
384 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400385 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800386 self._topdir = topdir
387 self._abort_if_user_denies = abort_if_user_denies
388
389 # Store the full path to the script for convenience.
390 if self._hooks_project:
391 self._script_fullpath = os.path.join(self._hooks_project.worktree,
392 self._hook_type + '.py')
393 else:
394 self._script_fullpath = None
395
396 def _GetHash(self):
397 """Return a hash of the contents of the hooks directory.
398
399 We'll just use git to do this. This hash has the property that if anything
400 changes in the directory we will return a different has.
401
402 SECURITY CONSIDERATION:
403 This hash only represents the contents of files in the hook directory, not
404 any other files imported or called by hooks. Changes to imported files
405 can change the script behavior without affecting the hash.
406
407 Returns:
408 A string representing the hash. This will always be ASCII so that it can
409 be printed to the user easily.
410 """
411 assert self._hooks_project, "Must have hooks to calculate their hash."
412
413 # We will use the work_git object rather than just calling GetRevisionId().
414 # That gives us a hash of the latest checked in version of the files that
415 # the user will actually be executing. Specifically, GetRevisionId()
416 # doesn't appear to change even if a user checks out a different version
417 # of the hooks repo (via git checkout) nor if a user commits their own revs.
418 #
419 # NOTE: Local (non-committed) changes will not be factored into this hash.
420 # I think this is OK, since we're really only worried about warning the user
421 # about upstream changes.
422 return self._hooks_project.work_git.rev_parse('HEAD')
423
424 def _GetMustVerb(self):
425 """Return 'must' if the hook is required; 'should' if not."""
426 if self._abort_if_user_denies:
427 return 'must'
428 else:
429 return 'should'
430
431 def _CheckForHookApproval(self):
432 """Check to see whether this hook has been approved.
433
Mike Frysinger40252c22016-08-15 21:23:44 -0400434 We'll accept approval of manifest URLs if they're using secure transports.
435 This way the user can say they trust the manifest hoster. For insecure
436 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800437
438 Note that we ask permission for each individual hook even though we use
439 the hash of all hooks when detecting changes. We'd like the user to be
440 able to approve / deny each hook individually. We only use the hash of all
441 hooks because there is no other easy way to detect changes to local imports.
442
443 Returns:
444 True if this hook is approved to run; False otherwise.
445
446 Raises:
447 HookError: Raised if the user doesn't approve and abort_if_user_denies
448 was passed to the consturctor.
449 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400450 if self._ManifestUrlHasSecureScheme():
451 return self._CheckForHookApprovalManifest()
452 else:
453 return self._CheckForHookApprovalHash()
454
455 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
456 changed_prompt):
457 """Check for approval for a particular attribute and hook.
458
459 Args:
460 subkey: The git config key under [repo.hooks.<hook_type>] to store the
461 last approved string.
462 new_val: The new value to compare against the last approved one.
463 main_prompt: Message to display to the user to ask for approval.
464 changed_prompt: Message explaining why we're re-asking for approval.
465
466 Returns:
467 True if this hook is approved to run; False otherwise.
468
469 Raises:
470 HookError: Raised if the user doesn't approve and abort_if_user_denies
471 was passed to the consturctor.
472 """
Doug Anderson37282b42011-03-04 11:54:18 -0800473 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400474 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800475
Mike Frysinger40252c22016-08-15 21:23:44 -0400476 # Get the last value that the user approved for this hook; may be None.
477 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800478
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800480 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400481 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800482 # Approval matched. We're done.
483 return True
484 else:
485 # Give the user a reason why we're prompting, since they last told
486 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400487 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800488 else:
489 prompt = ''
490
491 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
492 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400493 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530494 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900495 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800496
497 # User is doing a one-time approval.
498 if response in ('y', 'yes'):
499 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400500 elif response == 'always':
501 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800502 return True
503
504 # For anything else, we'll assume no approval.
505 if self._abort_if_user_denies:
506 raise HookError('You must allow the %s hook or use --no-verify.' %
507 self._hook_type)
508
509 return False
510
Mike Frysinger40252c22016-08-15 21:23:44 -0400511 def _ManifestUrlHasSecureScheme(self):
512 """Check if the URI for the manifest is a secure transport."""
513 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
514 parse_results = urllib.parse.urlparse(self._manifest_url)
515 return parse_results.scheme in secure_schemes
516
517 def _CheckForHookApprovalManifest(self):
518 """Check whether the user has approved this manifest host.
519
520 Returns:
521 True if this hook is approved to run; False otherwise.
522 """
523 return self._CheckForHookApprovalHelper(
524 'approvedmanifest',
525 self._manifest_url,
526 'Run hook scripts from %s' % (self._manifest_url,),
527 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
528
529 def _CheckForHookApprovalHash(self):
530 """Check whether the user has approved the hooks repo.
531
532 Returns:
533 True if this hook is approved to run; False otherwise.
534 """
535 prompt = ('Repo %s run the script:\n'
536 ' %s\n'
537 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700538 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400539 return self._CheckForHookApprovalHelper(
540 'approvedhash',
541 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700542 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400543 'Scripts have changed since %s was allowed.' % (self._hook_type,))
544
Doug Anderson37282b42011-03-04 11:54:18 -0800545 def _ExecuteHook(self, **kwargs):
546 """Actually execute the given hook.
547
548 This will run the hook's 'main' function in our python interpreter.
549
550 Args:
551 kwargs: Keyword arguments to pass to the hook. These are often specific
552 to the hook type. For instance, pre-upload hooks will contain
553 a project_list.
554 """
555 # Keep sys.path and CWD stashed away so that we can always restore them
556 # upon function exit.
557 orig_path = os.getcwd()
558 orig_syspath = sys.path
559
560 try:
561 # Always run hooks with CWD as topdir.
562 os.chdir(self._topdir)
563
564 # Put the hook dir as the first item of sys.path so hooks can do
565 # relative imports. We want to replace the repo dir as [0] so
566 # hooks can't import repo files.
567 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
568
569 # Exec, storing global context in the context dict. We catch exceptions
570 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500571 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800572 try:
Anthony King70f68902014-05-05 21:15:34 +0100573 exec(compile(open(self._script_fullpath).read(),
574 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800575 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700576 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
577 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800578
579 # Running the script should have defined a main() function.
580 if 'main' not in context:
581 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
582
Doug Anderson37282b42011-03-04 11:54:18 -0800583 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
584 # We don't actually want hooks to define their main with this argument--
585 # it's there to remind them that their hook should always take **kwargs.
586 # For instance, a pre-upload hook should be defined like:
587 # def main(project_list, **kwargs):
588 #
589 # This allows us to later expand the API without breaking old hooks.
590 kwargs = kwargs.copy()
591 kwargs['hook_should_take_kwargs'] = True
592
593 # Call the main function in the hook. If the hook should cause the
594 # build to fail, it will raise an Exception. We'll catch that convert
595 # to a HookError w/ just the failing traceback.
596 try:
597 context['main'](**kwargs)
598 except Exception:
599 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700600 'above.' % (traceback.format_exc(),
601 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800602 finally:
603 # Restore sys.path and CWD.
604 sys.path = orig_syspath
605 os.chdir(orig_path)
606
607 def Run(self, user_allows_all_hooks, **kwargs):
608 """Run the hook.
609
610 If the hook doesn't exist (because there is no hooks project or because
611 this particular hook is not enabled), this is a no-op.
612
613 Args:
614 user_allows_all_hooks: If True, we will never prompt about running the
615 hook--we'll just assume it's OK to run it.
616 kwargs: Keyword arguments to pass to the hook. These are often specific
617 to the hook type. For instance, pre-upload hooks will contain
618 a project_list.
619
620 Raises:
621 HookError: If there was a problem finding the hook or the user declined
622 to run a required hook (from _CheckForHookApproval).
623 """
624 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700625 if ((not self._hooks_project) or (self._hook_type not in
626 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800627 return
628
629 # Bail with a nice error if we can't find the hook.
630 if not os.path.isfile(self._script_fullpath):
631 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
632
633 # Make sure the user is OK with running the hook.
634 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
635 return
636
637 # Run the hook with the same version of python we're using.
638 self._ExecuteHook(**kwargs)
639
640
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700641class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600642 # These objects can be shared between several working trees.
643 shareable_files = ['description', 'info']
644 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
645 # These objects can only be used by a single working tree.
646 working_tree_files = ['config', 'packed-refs', 'shallow']
647 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700648
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700649 def __init__(self,
650 manifest,
651 name,
652 remote,
653 gitdir,
David James8d201162013-10-11 17:03:19 -0700654 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700655 worktree,
656 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700657 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800658 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100659 rebase=True,
660 groups=None,
661 sync_c=False,
662 sync_s=False,
663 clone_depth=None,
664 upstream=None,
665 parent=None,
666 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900667 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700668 optimized_fetch=False,
669 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800670 """Init a Project object.
671
672 Args:
673 manifest: The XmlManifest object.
674 name: The `name` attribute of manifest.xml's project element.
675 remote: RemoteSpec object specifying its remote's properties.
676 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700677 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800678 worktree: Absolute path of git working tree.
679 relpath: Relative path of git working tree to repo's top directory.
680 revisionExpr: The `revision` attribute of manifest.xml's project element.
681 revisionId: git commit id for checking out.
682 rebase: The `rebase` attribute of manifest.xml's project element.
683 groups: The `groups` attribute of manifest.xml's project element.
684 sync_c: The `sync-c` attribute of manifest.xml's project element.
685 sync_s: The `sync-s` attribute of manifest.xml's project element.
686 upstream: The `upstream` attribute of manifest.xml's project element.
687 parent: The parent Project object.
688 is_derived: False if the project was explicitly defined in the manifest;
689 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400690 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900691 optimized_fetch: If True, when a project is set to a sha1 revision, only
692 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700693 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800694 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700695 self.manifest = manifest
696 self.name = name
697 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800698 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700699 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800700 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700701 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800702 else:
703 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700704 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700705 self.revisionExpr = revisionExpr
706
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700707 if revisionId is None \
708 and revisionExpr \
709 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700710 self.revisionId = revisionExpr
711 else:
712 self.revisionId = revisionId
713
Mike Pontillod3153822012-02-28 11:53:24 -0800714 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700715 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700716 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800717 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900718 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700719 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800720 self.parent = parent
721 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900722 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800723 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800724
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700725 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500727 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500728 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700729 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
730 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700731
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800732 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700733 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800734 else:
735 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700736 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700737 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700738 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400739 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700740 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700741
Doug Anderson37282b42011-03-04 11:54:18 -0800742 # This will be filled in if a project is later identified to be the
743 # project containing repo hooks.
744 self.enabled_repo_hooks = []
745
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700746 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800747 def Derived(self):
748 return self.is_derived
749
750 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700751 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600752 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700753
754 @property
755 def CurrentBranch(self):
756 """Obtain the name of the currently checked out branch.
757 The branch name omits the 'refs/heads/' prefix.
758 None is returned if the project is on a detached HEAD.
759 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700760 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700761 if b.startswith(R_HEADS):
762 return b[len(R_HEADS):]
763 return None
764
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700765 def IsRebaseInProgress(self):
766 w = self.worktree
767 g = os.path.join(w, '.git')
768 return os.path.exists(os.path.join(g, 'rebase-apply')) \
769 or os.path.exists(os.path.join(g, 'rebase-merge')) \
770 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200771
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700772 def IsDirty(self, consider_untracked=True):
773 """Is the working directory modified in some way?
774 """
775 self.work_git.update_index('-q',
776 '--unmerged',
777 '--ignore-missing',
778 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900779 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700780 return True
781 if self.work_git.DiffZ('diff-files'):
782 return True
783 if consider_untracked and self.work_git.LsOthers():
784 return True
785 return False
786
787 _userident_name = None
788 _userident_email = None
789
790 @property
791 def UserName(self):
792 """Obtain the user's personal name.
793 """
794 if self._userident_name is None:
795 self._LoadUserIdentity()
796 return self._userident_name
797
798 @property
799 def UserEmail(self):
800 """Obtain the user's email address. This is very likely
801 to be their Gerrit login.
802 """
803 if self._userident_email is None:
804 self._LoadUserIdentity()
805 return self._userident_email
806
807 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900808 u = self.bare_git.var('GIT_COMMITTER_IDENT')
809 m = re.compile("^(.*) <([^>]*)> ").match(u)
810 if m:
811 self._userident_name = m.group(1)
812 self._userident_email = m.group(2)
813 else:
814 self._userident_name = ''
815 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700816
817 def GetRemote(self, name):
818 """Get the configuration for a single remote.
819 """
820 return self.config.GetRemote(name)
821
822 def GetBranch(self, name):
823 """Get the configuration for a single branch.
824 """
825 return self.config.GetBranch(name)
826
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700827 def GetBranches(self):
828 """Get all existing local branches.
829 """
830 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900831 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700832 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700833
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530834 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700835 if name.startswith(R_HEADS):
836 name = name[len(R_HEADS):]
837 b = self.GetBranch(name)
838 b.current = name == current
839 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900840 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700841 heads[name] = b
842
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530843 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700844 if name.startswith(R_PUB):
845 name = name[len(R_PUB):]
846 b = heads.get(name)
847 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900848 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700849
850 return heads
851
Colin Cross5acde752012-03-28 20:15:45 -0700852 def MatchesGroups(self, manifest_groups):
853 """Returns true if the manifest groups specified at init should cause
854 this project to be synced.
855 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700856 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700857
858 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700859 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700860 manifest_groups: "-group1,group2"
861 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500862
863 The special manifest group "default" will match any project that
864 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700865 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500866 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700867 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700868 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500869 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700870
Conley Owens971de8e2012-04-16 10:36:08 -0700871 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700872 for group in expanded_manifest_groups:
873 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700874 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700875 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700876 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700877
Conley Owens971de8e2012-04-16 10:36:08 -0700878 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700880# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700881 def UncommitedFiles(self, get_all=True):
882 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700883
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700884 Args:
885 get_all: a boolean, if True - get information about all different
886 uncommitted files. If False - return as soon as any kind of
887 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500888 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700889 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500890 self.work_git.update_index('-q',
891 '--unmerged',
892 '--ignore-missing',
893 '--refresh')
894 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700895 details.append("rebase in progress")
896 if not get_all:
897 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500898
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700899 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
900 if changes:
901 details.extend(changes)
902 if not get_all:
903 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500904
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700905 changes = self.work_git.DiffZ('diff-files').keys()
906 if changes:
907 details.extend(changes)
908 if not get_all:
909 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500910
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700911 changes = self.work_git.LsOthers()
912 if changes:
913 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500914
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700915 return details
916
917 def HasChanges(self):
918 """Returns true if there are uncommitted changes.
919 """
920 if self.UncommitedFiles(get_all=False):
921 return True
922 else:
923 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500924
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600925 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700926 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200927
928 Args:
929 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600930 quiet: If True then only print the project name. Do not print
931 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932 """
933 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700934 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200935 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700936 print(file=output_redir)
937 print('project %s/' % self.relpath, file=output_redir)
938 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939 return
940
941 self.work_git.update_index('-q',
942 '--unmerged',
943 '--ignore-missing',
944 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700945 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700946 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
947 df = self.work_git.DiffZ('diff-files')
948 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100949 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700950 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700951
952 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700953 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200954 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700955 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700956
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600957 if quiet:
958 out.nl()
959 return 'DIRTY'
960
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961 branch = self.CurrentBranch
962 if branch is None:
963 out.nobranch('(*** NO BRANCH ***)')
964 else:
965 out.branch('branch %s', branch)
966 out.nl()
967
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700968 if rb:
969 out.important('prior sync failed; rebase still in progress')
970 out.nl()
971
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700972 paths = list()
973 paths.extend(di.keys())
974 paths.extend(df.keys())
975 paths.extend(do)
976
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530977 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900978 try:
979 i = di[p]
980 except KeyError:
981 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700982
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900983 try:
984 f = df[p]
985 except KeyError:
986 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200987
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900988 if i:
989 i_status = i.status.upper()
990 else:
991 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700992
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900993 if f:
994 f_status = f.status.lower()
995 else:
996 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997
998 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800999 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001000 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001001 else:
1002 line = ' %s%s\t%s' % (i_status, f_status, p)
1003
1004 if i and not f:
1005 out.added('%s', line)
1006 elif (i and f) or (not i and f):
1007 out.changed('%s', line)
1008 elif not i and not f:
1009 out.untracked('%s', line)
1010 else:
1011 out.write('%s', line)
1012 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001013
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001014 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001015
pelyad67872d2012-03-28 14:49:58 +03001016 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001017 """Prints the status of the repository to stdout.
1018 """
1019 out = DiffColoring(self.config)
1020 cmd = ['diff']
1021 if out.is_on:
1022 cmd.append('--color')
1023 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001024 if absolute_paths:
1025 cmd.append('--src-prefix=a/%s/' % self.relpath)
1026 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001027 cmd.append('--')
1028 p = GitCommand(self,
1029 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001030 capture_stdout=True,
1031 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001032 has_diff = False
1033 for line in p.process.stdout:
1034 if not has_diff:
1035 out.nl()
1036 out.project('project %s/' % self.relpath)
1037 out.nl()
1038 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001039 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040 p.Wait()
1041
1042
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001043# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001044
David Pursehouse8a68ff92012-09-24 12:15:13 +09001045 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001046 """Was the branch published (uploaded) for code review?
1047 If so, returns the SHA-1 hash of the last published
1048 state for the branch.
1049 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001050 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001051 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001052 try:
1053 return self.bare_git.rev_parse(key)
1054 except GitError:
1055 return None
1056 else:
1057 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001058 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001059 except KeyError:
1060 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001061
David Pursehouse8a68ff92012-09-24 12:15:13 +09001062 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 """Prunes any stale published refs.
1064 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001065 if all_refs is None:
1066 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067 heads = set()
1068 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301069 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070 if name.startswith(R_HEADS):
1071 heads.add(name)
1072 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001073 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301075 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001076 n = name[len(R_PUB):]
1077 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001078 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001079
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001080 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001081 """List any branches which can be uploaded for review.
1082 """
1083 heads = {}
1084 pubed = {}
1085
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301086 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001088 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001089 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001090 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091
1092 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301093 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001094 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001096 if selected_branch and branch != selected_branch:
1097 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001099 rb = self.GetUploadableBranch(branch)
1100 if rb:
1101 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001102 return ready
1103
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001104 def GetUploadableBranch(self, branch_name):
1105 """Get a single uploadable branch, or None.
1106 """
1107 branch = self.GetBranch(branch_name)
1108 base = branch.LocalMerge
1109 if branch.LocalMerge:
1110 rb = ReviewableBranch(self, branch, base)
1111 if rb.commits:
1112 return rb
1113 return None
1114
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001115 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001116 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001117 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001118 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001119 private=False,
1120 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001121 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001122 validate_certs=True,
1123 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001124 """Uploads the named branch for code review.
1125 """
1126 if branch is None:
1127 branch = self.CurrentBranch
1128 if branch is None:
1129 raise GitError('not currently on a branch')
1130
1131 branch = self.GetBranch(branch)
1132 if not branch.LocalMerge:
1133 raise GitError('branch %s does not track a remote' % branch.name)
1134 if not branch.remote.review:
1135 raise GitError('remote %s has no review url' % branch.remote.name)
1136
Bryan Jacobsf609f912013-05-06 13:36:24 -04001137 if dest_branch is None:
1138 dest_branch = self.dest_branch
1139 if dest_branch is None:
1140 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001141 if not dest_branch.startswith(R_HEADS):
1142 dest_branch = R_HEADS + dest_branch
1143
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001144 if not branch.remote.projectname:
1145 branch.remote.projectname = self.name
1146 branch.remote.Save()
1147
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001148 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001149 if url is None:
1150 raise UploadError('review not configured')
1151 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001152
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001153 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001154 rp = ['gerrit receive-pack']
1155 for e in people[0]:
1156 rp.append('--reviewer=%s' % sq(e))
1157 for e in people[1]:
1158 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001159 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001160
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001161 for push_option in (push_options or []):
1162 cmd.append('-o')
1163 cmd.append(push_option)
1164
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001165 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001166
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 if dest_branch.startswith(R_HEADS):
1168 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001169
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001170 upload_type = 'for'
1171 if draft:
1172 upload_type = 'drafts'
1173
1174 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1175 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001176 if auto_topic:
1177 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001178
Shawn Pearce45d21682013-02-28 00:35:51 -08001179 if not url.startswith('ssh://'):
1180 rp = ['r=%s' % p for p in people[0]] + \
1181 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001182 if private:
1183 rp = rp + ['private']
1184 if wip:
1185 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001186 if rp:
1187 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001188 cmd.append(ref_spec)
1189
Anthony King7bdac712014-07-16 12:56:40 +01001190 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001191 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192
1193 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1194 self.bare_git.UpdateRef(R_PUB + branch.name,
1195 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001196 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001197
1198
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001199# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001200
Julien Campergue335f5ef2013-10-16 11:02:35 +02001201 def _ExtractArchive(self, tarpath, path=None):
1202 """Extract the given tar on its current location
1203
1204 Args:
1205 - tarpath: The path to the actual tar file
1206
1207 """
1208 try:
1209 with tarfile.open(tarpath, 'r') as tar:
1210 tar.extractall(path=path)
1211 return True
1212 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001213 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001214 return False
1215
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001216 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001217 quiet=False,
1218 is_new=None,
1219 current_branch_only=False,
1220 force_sync=False,
1221 clone_bundle=True,
1222 no_tags=False,
1223 archive=False,
1224 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001225 prune=False,
1226 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001227 """Perform only the network IO portion of the sync process.
1228 Local working directory/branch state is not affected.
1229 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001230 if archive and not isinstance(self, MetaProject):
1231 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001232 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001233 return False
1234
1235 name = self.relpath.replace('\\', '/')
1236 name = name.replace('/', '_')
1237 tarpath = '%s.tar' % name
1238 topdir = self.manifest.topdir
1239
1240 try:
1241 self._FetchArchive(tarpath, cwd=topdir)
1242 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001243 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001244 return False
1245
1246 # From now on, we only need absolute tarpath
1247 tarpath = os.path.join(topdir, tarpath)
1248
1249 if not self._ExtractArchive(tarpath, path=topdir):
1250 return False
1251 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001252 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001253 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001254 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001255 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001256 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001257 if is_new is None:
1258 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001259 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001260 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001261 else:
1262 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001263 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001264
1265 if is_new:
1266 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1267 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001268 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001269 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001270 # This works for both absolute and relative alternate directories.
1271 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001272 finally:
1273 fd.close()
1274 except IOError:
1275 alt_dir = None
1276 else:
1277 alt_dir = None
1278
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001279 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001280 and alt_dir is None \
1281 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001282 is_new = False
1283
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001284 if not current_branch_only:
1285 if self.sync_c:
1286 current_branch_only = True
1287 elif not self.manifest._loaded:
1288 # Manifest cannot check defaults until it syncs.
1289 current_branch_only = False
1290 elif self.manifest.default.sync_c:
1291 current_branch_only = True
1292
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001293 if self.clone_depth:
1294 depth = self.clone_depth
1295 else:
1296 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1297
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001298 need_to_fetch = not (optimized_fetch and
1299 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001300 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001301 if (need_to_fetch and
1302 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1303 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001304 no_tags=no_tags, prune=prune, depth=depth,
1305 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001306 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001307
1308 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001309 self._InitMRef()
1310 else:
1311 self._InitMirrorHead()
1312 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001313 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001314 except OSError:
1315 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001316 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001317
1318 def PostRepoUpgrade(self):
1319 self._InitHooks()
1320
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001321 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001322 if self.manifest.isGitcClient:
1323 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001324 for copyfile in self.copyfiles:
1325 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001326 for linkfile in self.linkfiles:
1327 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001328
Julien Camperguedd654222014-01-09 16:21:37 +01001329 def GetCommitRevisionId(self):
1330 """Get revisionId of a commit.
1331
1332 Use this method instead of GetRevisionId to get the id of the commit rather
1333 than the id of the current git object (for example, a tag)
1334
1335 """
1336 if not self.revisionExpr.startswith(R_TAGS):
1337 return self.GetRevisionId(self._allrefs)
1338
1339 try:
1340 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1341 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001342 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1343 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001344
David Pursehouse8a68ff92012-09-24 12:15:13 +09001345 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001346 if self.revisionId:
1347 return self.revisionId
1348
1349 rem = self.GetRemote(self.remote.name)
1350 rev = rem.ToLocal(self.revisionExpr)
1351
David Pursehouse8a68ff92012-09-24 12:15:13 +09001352 if all_refs is not None and rev in all_refs:
1353 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001354
1355 try:
1356 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1357 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001358 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1359 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001360
Martin Kellye4e94d22017-03-21 16:05:12 -07001361 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001362 """Perform only the local IO portion of the sync process.
1363 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001364 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001365 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001366 all_refs = self.bare_ref.all
1367 self.CleanPublishedCache(all_refs)
1368 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001369
David Pursehouse1d947b32012-10-25 12:23:11 +09001370 def _doff():
1371 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001372 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001373
Martin Kellye4e94d22017-03-21 16:05:12 -07001374 def _dosubmodules():
1375 self._SyncSubmodules(quiet=True)
1376
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001377 head = self.work_git.GetHead()
1378 if head.startswith(R_HEADS):
1379 branch = head[len(R_HEADS):]
1380 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001381 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001382 except KeyError:
1383 head = None
1384 else:
1385 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001386
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001387 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001388 # Currently on a detached HEAD. The user is assumed to
1389 # not have any local modifications worth worrying about.
1390 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001391 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001392 syncbuf.fail(self, _PriorSyncFailedError())
1393 return
1394
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001395 if head == revid:
1396 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001397 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001398 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001399 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001400 # The copy/linkfile config may have changed.
1401 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001402 return
1403 else:
1404 lost = self._revlist(not_rev(revid), HEAD)
1405 if lost:
1406 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001407
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001408 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001409 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001410 if submodules:
1411 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001412 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001413 syncbuf.fail(self, e)
1414 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001415 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001416 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001417
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001418 if head == revid:
1419 # No changes; don't do anything further.
1420 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001421 # The copy/linkfile config may have changed.
1422 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001423 return
1424
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001425 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001426
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001427 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001428 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001429 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001430 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001431 syncbuf.info(self,
1432 "leaving %s; does not track upstream",
1433 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001435 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001436 if submodules:
1437 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001438 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001439 syncbuf.fail(self, e)
1440 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001441 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001442 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001444 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001445 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001446 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001447 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001448 if not_merged:
1449 if upstream_gain:
1450 # The user has published this branch and some of those
1451 # commits are not yet merged upstream. We do not want
1452 # to rewrite the published commits so we punt.
1453 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001454 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001455 "branch %s is published (but not merged) and is now "
1456 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001457 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001458 elif pub == head:
1459 # All published commits are merged, and thus we are a
1460 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001461 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001462 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001463 if submodules:
1464 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001465 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001466
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001467 # Examine the local commits not in the remote. Find the
1468 # last one attributed to this user, if any.
1469 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001470 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001471 last_mine = None
1472 cnt_mine = 0
1473 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301474 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001475 if committer_email == self.UserEmail:
1476 last_mine = commit_id
1477 cnt_mine += 1
1478
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001479 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001480 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001481
1482 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001483 syncbuf.fail(self, _DirtyError())
1484 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001485
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001486 # If the upstream switched on us, warn the user.
1487 #
1488 if branch.merge != self.revisionExpr:
1489 if branch.merge and self.revisionExpr:
1490 syncbuf.info(self,
1491 'manifest switched %s...%s',
1492 branch.merge,
1493 self.revisionExpr)
1494 elif branch.merge:
1495 syncbuf.info(self,
1496 'manifest no longer tracks %s',
1497 branch.merge)
1498
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001499 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001500 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001501 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001503 syncbuf.info(self,
1504 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001505 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001507 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001508 if not ID_RE.match(self.revisionExpr):
1509 # in case of manifest sync the revisionExpr might be a SHA1
1510 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001511 if not branch.merge.startswith('refs/'):
1512 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001513 branch.Save()
1514
Mike Pontillod3153822012-02-28 11:53:24 -08001515 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001516 def _docopyandlink():
1517 self._CopyAndLinkFiles()
1518
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001519 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001520 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001521 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001522 if submodules:
1523 syncbuf.later2(self, _dosubmodules)
1524 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001525 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001526 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001527 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001528 if submodules:
1529 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001530 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001531 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001532 syncbuf.fail(self, e)
1533 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001534 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001535 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001536 if submodules:
1537 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001538
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001539 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001540 # dest should already be an absolute path, but src is project relative
1541 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001542 abssrc = os.path.join(self.worktree, src)
1543 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001544
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001545 def AddLinkFile(self, src, dest, absdest):
1546 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001547 # make src relative path to dest
1548 absdestdir = os.path.dirname(absdest)
1549 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001550 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001551
James W. Mills24c13082012-04-12 15:04:13 -05001552 def AddAnnotation(self, name, value, keep):
1553 self.annotations.append(_Annotation(name, value, keep))
1554
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001555 def DownloadPatchSet(self, change_id, patch_id):
1556 """Download a single patch set of a single change to FETCH_HEAD.
1557 """
1558 remote = self.GetRemote(self.remote.name)
1559
1560 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001561 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001562 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001563 if GitCommand(self, cmd, bare=True).Wait() != 0:
1564 return None
1565 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001566 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001567 change_id,
1568 patch_id,
1569 self.bare_git.rev_parse('FETCH_HEAD'))
1570
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001571
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001572# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001573
Simran Basib9a1b732015-08-20 12:19:28 -07001574 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001575 """Create a new branch off the manifest's revision.
1576 """
Simran Basib9a1b732015-08-20 12:19:28 -07001577 if not branch_merge:
1578 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001579 head = self.work_git.GetHead()
1580 if head == (R_HEADS + name):
1581 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582
David Pursehouse8a68ff92012-09-24 12:15:13 +09001583 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001584 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001585 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001586 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001587 capture_stdout=True,
1588 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001589
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001590 branch = self.GetBranch(name)
1591 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001592 branch.merge = branch_merge
1593 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1594 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001595 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001596
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001597 if head.startswith(R_HEADS):
1598 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001599 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001600 except KeyError:
1601 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001602 if revid and head and revid == head:
1603 ref = os.path.join(self.gitdir, R_HEADS + name)
1604 try:
1605 os.makedirs(os.path.dirname(ref))
1606 except OSError:
1607 pass
1608 _lwrite(ref, '%s\n' % revid)
1609 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1610 'ref: %s%s\n' % (R_HEADS, name))
1611 branch.Save()
1612 return True
1613
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001614 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001615 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001616 capture_stdout=True,
1617 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001618 branch.Save()
1619 return True
1620 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001621
Wink Saville02d79452009-04-10 13:01:24 -07001622 def CheckoutBranch(self, name):
1623 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001624
1625 Args:
1626 name: The name of the branch to checkout.
1627
1628 Returns:
1629 True if the checkout succeeded; False if it didn't; None if the branch
1630 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001631 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001632 rev = R_HEADS + name
1633 head = self.work_git.GetHead()
1634 if head == rev:
1635 # Already on the branch
1636 #
1637 return True
Wink Saville02d79452009-04-10 13:01:24 -07001638
David Pursehouse8a68ff92012-09-24 12:15:13 +09001639 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001640 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001641 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001642 except KeyError:
1643 # Branch does not exist in this project
1644 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001645 return None
Wink Saville02d79452009-04-10 13:01:24 -07001646
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001647 if head.startswith(R_HEADS):
1648 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001649 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001650 except KeyError:
1651 head = None
1652
1653 if head == revid:
1654 # Same revision; just update HEAD to point to the new
1655 # target branch, but otherwise take no other action.
1656 #
1657 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1658 'ref: %s%s\n' % (R_HEADS, name))
1659 return True
1660
1661 return GitCommand(self,
1662 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001663 capture_stdout=True,
1664 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001665
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001666 def AbandonBranch(self, name):
1667 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001668
1669 Args:
1670 name: The name of the branch to abandon.
1671
1672 Returns:
1673 True if the abandon succeeded; False if it didn't; None if the branch
1674 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001675 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001676 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001677 all_refs = self.bare_ref.all
1678 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001679 # Doesn't exist
1680 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001681
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001682 head = self.work_git.GetHead()
1683 if head == rev:
1684 # We can't destroy the branch while we are sitting
1685 # on it. Switch to a detached HEAD.
1686 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001687 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001688
David Pursehouse8a68ff92012-09-24 12:15:13 +09001689 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001690 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001691 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1692 '%s\n' % revid)
1693 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001694 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001695
1696 return GitCommand(self,
1697 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001698 capture_stdout=True,
1699 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001700
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001701 def PruneHeads(self):
1702 """Prune any topic branches already merged into upstream.
1703 """
1704 cb = self.CurrentBranch
1705 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001706 left = self._allrefs
1707 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001708 if name.startswith(R_HEADS):
1709 name = name[len(R_HEADS):]
1710 if cb is None or name != cb:
1711 kill.append(name)
1712
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001713 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001714 if cb is not None \
1715 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001716 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001717 self.work_git.DetachHead(HEAD)
1718 kill.append(cb)
1719
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001720 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001721 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001722
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723 try:
1724 self.bare_git.DetachHead(rev)
1725
1726 b = ['branch', '-d']
1727 b.extend(kill)
1728 b = GitCommand(self, b, bare=True,
1729 capture_stdout=True,
1730 capture_stderr=True)
1731 b.Wait()
1732 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001733 if ID_RE.match(old):
1734 self.bare_git.DetachHead(old)
1735 else:
1736 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001737 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001738
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001739 for branch in kill:
1740 if (R_HEADS + branch) not in left:
1741 self.CleanPublishedCache()
1742 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001743
1744 if cb and cb not in kill:
1745 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001746 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001747
1748 kept = []
1749 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001750 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001751 branch = self.GetBranch(branch)
1752 base = branch.LocalMerge
1753 if not base:
1754 base = rev
1755 kept.append(ReviewableBranch(self, branch, base))
1756 return kept
1757
1758
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001759# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001760
1761 def GetRegisteredSubprojects(self):
1762 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001763
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001764 def rec(subprojects):
1765 if not subprojects:
1766 return
1767 result.extend(subprojects)
1768 for p in subprojects:
1769 rec(p.subprojects)
1770 rec(self.subprojects)
1771 return result
1772
1773 def _GetSubmodules(self):
1774 # Unfortunately we cannot call `git submodule status --recursive` here
1775 # because the working tree might not exist yet, and it cannot be used
1776 # without a working tree in its current implementation.
1777
1778 def get_submodules(gitdir, rev):
1779 # Parse .gitmodules for submodule sub_paths and sub_urls
1780 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1781 if not sub_paths:
1782 return []
1783 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1784 # revision of submodule repository
1785 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1786 submodules = []
1787 for sub_path, sub_url in zip(sub_paths, sub_urls):
1788 try:
1789 sub_rev = sub_revs[sub_path]
1790 except KeyError:
1791 # Ignore non-exist submodules
1792 continue
1793 submodules.append((sub_rev, sub_path, sub_url))
1794 return submodules
1795
1796 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1797 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001798
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001799 def parse_gitmodules(gitdir, rev):
1800 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1801 try:
Anthony King7bdac712014-07-16 12:56:40 +01001802 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1803 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001804 except GitError:
1805 return [], []
1806 if p.Wait() != 0:
1807 return [], []
1808
1809 gitmodules_lines = []
1810 fd, temp_gitmodules_path = tempfile.mkstemp()
1811 try:
1812 os.write(fd, p.stdout)
1813 os.close(fd)
1814 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001815 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1816 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001817 if p.Wait() != 0:
1818 return [], []
1819 gitmodules_lines = p.stdout.split('\n')
1820 except GitError:
1821 return [], []
1822 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001823 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001824
1825 names = set()
1826 paths = {}
1827 urls = {}
1828 for line in gitmodules_lines:
1829 if not line:
1830 continue
1831 m = re_path.match(line)
1832 if m:
1833 names.add(m.group(1))
1834 paths[m.group(1)] = m.group(2)
1835 continue
1836 m = re_url.match(line)
1837 if m:
1838 names.add(m.group(1))
1839 urls[m.group(1)] = m.group(2)
1840 continue
1841 names = sorted(names)
1842 return ([paths.get(name, '') for name in names],
1843 [urls.get(name, '') for name in names])
1844
1845 def git_ls_tree(gitdir, rev, paths):
1846 cmd = ['ls-tree', rev, '--']
1847 cmd.extend(paths)
1848 try:
Anthony King7bdac712014-07-16 12:56:40 +01001849 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1850 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001851 except GitError:
1852 return []
1853 if p.Wait() != 0:
1854 return []
1855 objects = {}
1856 for line in p.stdout.split('\n'):
1857 if not line.strip():
1858 continue
1859 object_rev, object_path = line.split()[2:4]
1860 objects[object_path] = object_rev
1861 return objects
1862
1863 try:
1864 rev = self.GetRevisionId()
1865 except GitError:
1866 return []
1867 return get_submodules(self.gitdir, rev)
1868
1869 def GetDerivedSubprojects(self):
1870 result = []
1871 if not self.Exists:
1872 # If git repo does not exist yet, querying its submodules will
1873 # mess up its states; so return here.
1874 return result
1875 for rev, path, url in self._GetSubmodules():
1876 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001877 relpath, worktree, gitdir, objdir = \
1878 self.manifest.GetSubprojectPaths(self, name, path)
1879 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001880 if project:
1881 result.extend(project.GetDerivedSubprojects())
1882 continue
David James8d201162013-10-11 17:03:19 -07001883
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001884 if url.startswith('..'):
1885 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001886 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001887 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001888 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001889 review=self.remote.review,
1890 revision=self.remote.revision)
1891 subproject = Project(manifest=self.manifest,
1892 name=name,
1893 remote=remote,
1894 gitdir=gitdir,
1895 objdir=objdir,
1896 worktree=worktree,
1897 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001898 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001899 revisionId=rev,
1900 rebase=self.rebase,
1901 groups=self.groups,
1902 sync_c=self.sync_c,
1903 sync_s=self.sync_s,
1904 parent=self,
1905 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001906 result.append(subproject)
1907 result.extend(subproject.GetDerivedSubprojects())
1908 return result
1909
1910
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001911# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001912 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001913 try:
1914 # if revision (sha or tag) is not present then following function
1915 # throws an error.
1916 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1917 return True
1918 except GitError:
1919 # There is no such persistent revision. We have to fetch it.
1920 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001921
Julien Campergue335f5ef2013-10-16 11:02:35 +02001922 def _FetchArchive(self, tarpath, cwd=None):
1923 cmd = ['archive', '-v', '-o', tarpath]
1924 cmd.append('--remote=%s' % self.remote.url)
1925 cmd.append('--prefix=%s/' % self.relpath)
1926 cmd.append(self.revisionExpr)
1927
1928 command = GitCommand(self, cmd, cwd=cwd,
1929 capture_stdout=True,
1930 capture_stderr=True)
1931
1932 if command.Wait() != 0:
1933 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1934
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001935 def _RemoteFetch(self, name=None,
1936 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001937 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001938 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001939 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001940 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001941 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001942 depth=None,
1943 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001944
1945 is_sha1 = False
1946 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001947 # The depth should not be used when fetching to a mirror because
1948 # it will result in a shallow repository that cannot be cloned or
1949 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001950 # The repo project should also never be synced with partial depth.
1951 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1952 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001953
Shawn Pearce69e04d82014-01-29 12:48:54 -08001954 if depth:
1955 current_branch_only = True
1956
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001957 if ID_RE.match(self.revisionExpr) is not None:
1958 is_sha1 = True
1959
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001960 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001961 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001962 # this is a tag and its sha1 value should never change
1963 tag_name = self.revisionExpr[len(R_TAGS):]
1964
1965 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001966 if self._CheckForImmutableRevision():
1967 print('Skipped fetching project %s (already have persistent ref)'
1968 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001969 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001970 if is_sha1 and not depth:
1971 # When syncing a specific commit and --depth is not set:
1972 # * if upstream is explicitly specified and is not a sha1, fetch only
1973 # upstream as users expect only upstream to be fetch.
1974 # Note: The commit might not be in upstream in which case the sync
1975 # will fail.
1976 # * otherwise, fetch all branches to make sure we end up with the
1977 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001978 if self.upstream:
1979 current_branch_only = not ID_RE.match(self.upstream)
1980 else:
1981 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001982
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001983 if not name:
1984 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001985
1986 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001987 remote = self.GetRemote(name)
1988 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001989 ssh_proxy = True
1990
Shawn O. Pearce88443382010-10-08 10:02:09 +02001991 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001992 if alt_dir and 'objects' == os.path.basename(alt_dir):
1993 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001994 packed_refs = os.path.join(self.gitdir, 'packed-refs')
1995 remote = self.GetRemote(name)
1996
David Pursehouse8a68ff92012-09-24 12:15:13 +09001997 all_refs = self.bare_ref.all
1998 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02001999 tmp = set()
2000
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302001 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002002 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002003 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002004 all_refs[r] = ref_id
2005 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002006 continue
2007
David Pursehouse8a68ff92012-09-24 12:15:13 +09002008 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002009 continue
2010
David Pursehouse8a68ff92012-09-24 12:15:13 +09002011 r = 'refs/_alt/%s' % ref_id
2012 all_refs[r] = ref_id
2013 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 tmp.add(r)
2015
heping3d7bbc92017-04-12 19:51:47 +08002016 tmp_packed_lines = []
2017 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002018
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302019 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002020 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002021 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002022 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002023 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002024
heping3d7bbc92017-04-12 19:51:47 +08002025 tmp_packed = ''.join(tmp_packed_lines)
2026 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002027 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002028 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002029 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002030
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002031 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002032
Conley Owensf97e8382015-01-21 11:12:46 -08002033 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002034 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002035 else:
2036 # If this repo has shallow objects, then we don't know which refs have
2037 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2038 # do this with projects that don't have shallow objects, since it is less
2039 # efficient.
2040 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2041 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002042
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002043 if quiet:
2044 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002045 if not self.worktree:
2046 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002047 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002048
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002049 # If using depth then we should not get all the tags since they may
2050 # be outside of the depth.
2051 if no_tags or depth:
2052 cmd.append('--no-tags')
2053 else:
2054 cmd.append('--tags')
2055
David Pursehouse74cfd272015-10-14 10:50:15 +09002056 if prune:
2057 cmd.append('--prune')
2058
Martin Kellye4e94d22017-03-21 16:05:12 -07002059 if submodules:
2060 cmd.append('--recurse-submodules=on-demand')
2061
Conley Owens80b87fe2014-05-09 17:13:44 -07002062 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002063 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002064 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002065 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002066 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002067 spec.append('tag')
2068 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002069
David Pursehouse403b64e2015-04-27 10:41:33 +09002070 if not self.manifest.IsMirror:
2071 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002072 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002073 # Shallow checkout of a specific commit, fetch from that commit and not
2074 # the heads only as the commit might be deeper in the history.
2075 spec.append(branch)
2076 else:
2077 if is_sha1:
2078 branch = self.upstream
2079 if branch is not None and branch.strip():
2080 if not branch.startswith('refs/'):
2081 branch = R_HEADS + branch
2082 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002083 cmd.extend(spec)
2084
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002085 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002086 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002087 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002088 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002089 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002090 ok = True
2091 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002092 # If needed, run the 'git remote prune' the first time through the loop
2093 elif (not _i and
2094 "error:" in gitcmd.stderr and
2095 "git remote prune" in gitcmd.stderr):
2096 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002097 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002098 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002099 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002100 break
2101 continue
Brian Harring14a66742012-09-28 20:21:57 -07002102 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002103 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2104 # in sha1 mode, we just tried sync'ing from the upstream field; it
2105 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002106 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002107 elif ret < 0:
2108 # Git died with a signal, exit immediately
2109 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002110 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002111
2112 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002113 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002114 if old_packed != '':
2115 _lwrite(packed_refs, old_packed)
2116 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002117 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002118 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002119
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002120 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002121 # We just synced the upstream given branch; verify we
2122 # got what we wanted, else trigger a second run of all
2123 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002124 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002125 if current_branch_only and depth:
2126 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002127 return self._RemoteFetch(name=name,
2128 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002129 initial=False, quiet=quiet, alt_dir=alt_dir,
2130 depth=None)
2131 else:
2132 # Avoid infinite recursion: sync all branches with depth set to None
2133 return self._RemoteFetch(name=name, current_branch_only=False,
2134 initial=False, quiet=quiet, alt_dir=alt_dir,
2135 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002136
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002137 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002138
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002139 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002140 if initial and \
2141 (self.manifest.manifestProject.config.GetString('repo.depth') or
2142 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002143 return False
2144
2145 remote = self.GetRemote(self.remote.name)
2146 bundle_url = remote.url + '/clone.bundle'
2147 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002148 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2149 'persistent-http',
2150 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002151 return False
2152
2153 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2154 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2155
2156 exist_dst = os.path.exists(bundle_dst)
2157 exist_tmp = os.path.exists(bundle_tmp)
2158
2159 if not initial and not exist_dst and not exist_tmp:
2160 return False
2161
2162 if not exist_dst:
2163 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2164 if not exist_dst:
2165 return False
2166
2167 cmd = ['fetch']
2168 if quiet:
2169 cmd.append('--quiet')
2170 if not self.worktree:
2171 cmd.append('--update-head-ok')
2172 cmd.append(bundle_dst)
2173 for f in remote.fetch:
2174 cmd.append(str(f))
2175 cmd.append('refs/tags/*:refs/tags/*')
2176
2177 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002178 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002179 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002180 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002181 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002182 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002183
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002184 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002185 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002186 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002187
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002188 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002189 if quiet:
2190 cmd += ['--silent']
2191 if os.path.exists(tmpPath):
2192 size = os.stat(tmpPath).st_size
2193 if size >= 1024:
2194 cmd += ['--continue-at', '%d' % (size,)]
2195 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002196 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002197 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2198 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002199 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002200 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002201 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002202 if srcUrl.startswith('persistent-'):
2203 srcUrl = srcUrl[len('persistent-'):]
2204 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002205
Dave Borowitz137d0132015-01-02 11:12:54 -08002206 if IsTrace():
2207 Trace('%s', ' '.join(cmd))
2208 try:
2209 proc = subprocess.Popen(cmd)
2210 except OSError:
2211 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002212
Dave Borowitz137d0132015-01-02 11:12:54 -08002213 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002214
Dave Borowitz137d0132015-01-02 11:12:54 -08002215 if curlret == 22:
2216 # From curl man page:
2217 # 22: HTTP page not retrieved. The requested url was not found or
2218 # returned another error with the HTTP error code being 400 or above.
2219 # This return code only appears if -f, --fail is used.
2220 if not quiet:
2221 print("Server does not provide clone.bundle; ignoring.",
2222 file=sys.stderr)
2223 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002224
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002225 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002226 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002227 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002228 return True
2229 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002230 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002231 return False
2232 else:
2233 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002234
Kris Giesingc8d882a2014-12-23 13:02:32 -08002235 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002236 try:
2237 with open(path) as f:
2238 if f.read(16) == '# v2 git bundle\n':
2239 return True
2240 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002241 if not quiet:
2242 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002243 return False
2244 except OSError:
2245 return False
2246
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002247 def _Checkout(self, rev, quiet=False):
2248 cmd = ['checkout']
2249 if quiet:
2250 cmd.append('-q')
2251 cmd.append(rev)
2252 cmd.append('--')
2253 if GitCommand(self, cmd).Wait() != 0:
2254 if self._allrefs:
2255 raise GitError('%s checkout %s ' % (self.name, rev))
2256
Anthony King7bdac712014-07-16 12:56:40 +01002257 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002258 cmd = ['cherry-pick']
2259 cmd.append(rev)
2260 cmd.append('--')
2261 if GitCommand(self, cmd).Wait() != 0:
2262 if self._allrefs:
2263 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2264
Anthony King7bdac712014-07-16 12:56:40 +01002265 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002266 cmd = ['revert']
2267 cmd.append('--no-edit')
2268 cmd.append(rev)
2269 cmd.append('--')
2270 if GitCommand(self, cmd).Wait() != 0:
2271 if self._allrefs:
2272 raise GitError('%s revert %s ' % (self.name, rev))
2273
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002274 def _ResetHard(self, rev, quiet=True):
2275 cmd = ['reset', '--hard']
2276 if quiet:
2277 cmd.append('-q')
2278 cmd.append(rev)
2279 if GitCommand(self, cmd).Wait() != 0:
2280 raise GitError('%s reset --hard %s ' % (self.name, rev))
2281
Martin Kellye4e94d22017-03-21 16:05:12 -07002282 def _SyncSubmodules(self, quiet=True):
2283 cmd = ['submodule', 'update', '--init', '--recursive']
2284 if quiet:
2285 cmd.append('-q')
2286 if GitCommand(self, cmd).Wait() != 0:
2287 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2288
Anthony King7bdac712014-07-16 12:56:40 +01002289 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002290 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002291 if onto is not None:
2292 cmd.extend(['--onto', onto])
2293 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002294 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002295 raise GitError('%s rebase %s ' % (self.name, upstream))
2296
Pierre Tardy3d125942012-05-04 12:18:12 +02002297 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002298 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002299 if ffonly:
2300 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002301 if GitCommand(self, cmd).Wait() != 0:
2302 raise GitError('%s merge %s ' % (self.name, head))
2303
Kevin Degiabaa7f32014-11-12 11:27:45 -07002304 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002305 init_git_dir = not os.path.exists(self.gitdir)
2306 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002307 try:
2308 # Initialize the bare repository, which contains all of the objects.
2309 if init_obj_dir:
2310 os.makedirs(self.objdir)
2311 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002312
Kevin Degib1a07b82015-07-27 13:33:43 -06002313 # If we have a separate directory to hold refs, initialize it as well.
2314 if self.objdir != self.gitdir:
2315 if init_git_dir:
2316 os.makedirs(self.gitdir)
2317
2318 if init_obj_dir or init_git_dir:
2319 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2320 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002321 try:
2322 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2323 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002324 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002325 print("Retrying clone after deleting %s" %
2326 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002327 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002328 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2329 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002330 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002331 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002332 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2333 except:
2334 raise e
2335 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002336
Kevin Degi384b3c52014-10-16 16:02:58 -06002337 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002338 mp = self.manifest.manifestProject
2339 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002340
Kevin Degib1a07b82015-07-27 13:33:43 -06002341 if ref_dir or mirror_git:
2342 if not mirror_git:
2343 mirror_git = os.path.join(ref_dir, self.name + '.git')
2344 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2345 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002346
Kevin Degib1a07b82015-07-27 13:33:43 -06002347 if os.path.exists(mirror_git):
2348 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002349
Kevin Degib1a07b82015-07-27 13:33:43 -06002350 elif os.path.exists(repo_git):
2351 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002352
Kevin Degib1a07b82015-07-27 13:33:43 -06002353 else:
2354 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002355
Kevin Degib1a07b82015-07-27 13:33:43 -06002356 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002357 if not os.path.isabs(ref_dir):
2358 # The alternate directory is relative to the object database.
2359 ref_dir = os.path.relpath(ref_dir,
2360 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002361 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2362 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002363
Kevin Degib1a07b82015-07-27 13:33:43 -06002364 self._UpdateHooks()
2365
2366 m = self.manifest.manifestProject.config
2367 for key in ['user.name', 'user.email']:
2368 if m.Has(key, include_defaults=False):
2369 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002370 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002371 if self.manifest.IsMirror:
2372 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002373 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002374 self.config.SetString('core.bare', None)
2375 except Exception:
2376 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002377 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002378 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002379 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002380 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002381
Jimmie Westera0444582012-10-24 13:44:42 +02002382 def _UpdateHooks(self):
2383 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002384 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002385
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002386 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002387 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002388 if not os.path.exists(hooks):
2389 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002390 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002391 name = os.path.basename(stock_hook)
2392
Victor Boivie65e0f352011-04-18 11:23:29 +02002393 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002394 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002395 # Don't install a Gerrit Code Review hook if this
2396 # project does not appear to use it for reviews.
2397 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002398 # Since the manifest project is one of those, but also
2399 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002400 continue
2401
2402 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002403 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002404 continue
2405 if os.path.exists(dst):
2406 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002407 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002408 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002409 _warn("%s: Not replacing locally modified %s hook",
2410 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002411 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002412 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002413 platform_utils.symlink(
2414 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002415 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002416 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002417 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002418 else:
2419 raise
2420
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002421 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002422 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002423 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002424 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002425 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002426 remote.review = self.remote.review
2427 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002428
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002429 if self.worktree:
2430 remote.ResetFetch(mirror=False)
2431 else:
2432 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002433 remote.Save()
2434
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002435 def _InitMRef(self):
2436 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002437 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002438
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002439 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002440 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002441
2442 def _InitAnyMRef(self, ref):
2443 cur = self.bare_ref.symref(ref)
2444
2445 if self.revisionId:
2446 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2447 msg = 'manifest set to %s' % self.revisionId
2448 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002449 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002450 else:
2451 remote = self.GetRemote(self.remote.name)
2452 dst = remote.ToLocal(self.revisionExpr)
2453 if cur != dst:
2454 msg = 'manifest set to %s' % self.revisionExpr
2455 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002456
Kevin Degi384b3c52014-10-16 16:02:58 -06002457 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002458 symlink_files = self.shareable_files[:]
2459 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002460 if share_refs:
2461 symlink_files += self.working_tree_files
2462 symlink_dirs += self.working_tree_dirs
2463 to_symlink = symlink_files + symlink_dirs
2464 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002465 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002466 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002467 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002468 # Fail if the links are pointing to the wrong place
2469 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002470 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002471 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002472 'work tree. If you\'re comfortable with the '
2473 'possibility of losing the work tree\'s git metadata,'
2474 ' use `repo sync --force-sync {0}` to '
2475 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002476
David James8d201162013-10-11 17:03:19 -07002477 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2478 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2479
2480 Args:
2481 gitdir: The bare git repository. Must already be initialized.
2482 dotgit: The repository you would like to initialize.
2483 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2484 Only one work tree can store refs under a given |gitdir|.
2485 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2486 This saves you the effort of initializing |dotgit| yourself.
2487 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002488 symlink_files = self.shareable_files[:]
2489 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002490 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002491 symlink_files += self.working_tree_files
2492 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002493 to_symlink = symlink_files + symlink_dirs
2494
2495 to_copy = []
2496 if copy_all:
2497 to_copy = os.listdir(gitdir)
2498
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002499 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002500 for name in set(to_copy).union(to_symlink):
2501 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002502 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002503 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002504
Kevin Degi384b3c52014-10-16 16:02:58 -06002505 if os.path.lexists(dst):
2506 continue
David James8d201162013-10-11 17:03:19 -07002507
2508 # If the source dir doesn't exist, create an empty dir.
2509 if name in symlink_dirs and not os.path.lexists(src):
2510 os.makedirs(src)
2511
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002512 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002513 platform_utils.symlink(
2514 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002515 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002516 if os.path.isdir(src):
2517 shutil.copytree(src, dst)
2518 elif os.path.isfile(src):
2519 shutil.copy(src, dst)
2520
Conley Owens80b87fe2014-05-09 17:13:44 -07002521 # If the source file doesn't exist, ensure the destination
2522 # file doesn't either.
2523 if name in symlink_files and not os.path.lexists(src):
2524 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002525 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002526 except OSError:
2527 pass
2528
David James8d201162013-10-11 17:03:19 -07002529 except OSError as e:
2530 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002531 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002532 else:
2533 raise
2534
Martin Kellye4e94d22017-03-21 16:05:12 -07002535 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002536 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002537 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002538 try:
2539 if init_dotgit:
2540 os.makedirs(dotgit)
2541 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2542 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002543
Kevin Degiabaa7f32014-11-12 11:27:45 -07002544 try:
2545 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2546 except GitError as e:
2547 if force_sync:
2548 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002549 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002550 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002551 except:
2552 raise e
2553 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002554
Kevin Degib1a07b82015-07-27 13:33:43 -06002555 if init_dotgit:
2556 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002557
Kevin Degib1a07b82015-07-27 13:33:43 -06002558 cmd = ['read-tree', '--reset', '-u']
2559 cmd.append('-v')
2560 cmd.append(HEAD)
2561 if GitCommand(self, cmd).Wait() != 0:
2562 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002563
Martin Kellye4e94d22017-03-21 16:05:12 -07002564 if submodules:
2565 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002566 self._CopyAndLinkFiles()
2567 except Exception:
2568 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002569 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002570 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002571
Renaud Paquay788e9622017-01-27 11:41:12 -08002572 def _get_symlink_error_message(self):
2573 if platform_utils.isWindows():
2574 return ('Unable to create symbolic link. Please re-run the command as '
2575 'Administrator, or see '
2576 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2577 'for other options.')
2578 return 'filesystem must support symlinks'
2579
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002580 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002581 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002582
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002583 def _revlist(self, *args, **kw):
2584 a = []
2585 a.extend(args)
2586 a.append('--')
2587 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002588
2589 @property
2590 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002591 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002592
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002593 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002594 """Get logs between two revisions of this project."""
2595 comp = '..'
2596 if rev1:
2597 revs = [rev1]
2598 if rev2:
2599 revs.extend([comp, rev2])
2600 cmd = ['log', ''.join(revs)]
2601 out = DiffColoring(self.config)
2602 if out.is_on and color:
2603 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002604 if pretty_format is not None:
2605 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002606 if oneline:
2607 cmd.append('--oneline')
2608
2609 try:
2610 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2611 if log.Wait() == 0:
2612 return log.stdout
2613 except GitError:
2614 # worktree may not exist if groups changed for example. In that case,
2615 # try in gitdir instead.
2616 if not os.path.exists(self.worktree):
2617 return self.bare_git.log(*cmd[1:])
2618 else:
2619 raise
2620 return None
2621
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002622 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2623 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002624 """Get the list of logs from this revision to given revisionId"""
2625 logs = {}
2626 selfId = self.GetRevisionId(self._allrefs)
2627 toId = toProject.GetRevisionId(toProject._allrefs)
2628
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002629 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2630 pretty_format=pretty_format)
2631 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2632 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002633 return logs
2634
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002635 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002636
David James8d201162013-10-11 17:03:19 -07002637 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002638 self._project = project
2639 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002640 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002641
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002642 def LsOthers(self):
2643 p = GitCommand(self._project,
2644 ['ls-files',
2645 '-z',
2646 '--others',
2647 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002648 bare=False,
David James8d201162013-10-11 17:03:19 -07002649 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002650 capture_stdout=True,
2651 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002652 if p.Wait() == 0:
2653 out = p.stdout
2654 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002655 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002656 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002657 return []
2658
2659 def DiffZ(self, name, *args):
2660 cmd = [name]
2661 cmd.append('-z')
2662 cmd.extend(args)
2663 p = GitCommand(self._project,
2664 cmd,
David James8d201162013-10-11 17:03:19 -07002665 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002666 bare=False,
2667 capture_stdout=True,
2668 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002669 try:
2670 out = p.process.stdout.read()
2671 r = {}
2672 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002673 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002674 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002675 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002676 info = next(out)
2677 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002678 except StopIteration:
2679 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002680
2681 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002682
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002683 def __init__(self, path, omode, nmode, oid, nid, state):
2684 self.path = path
2685 self.src_path = None
2686 self.old_mode = omode
2687 self.new_mode = nmode
2688 self.old_id = oid
2689 self.new_id = nid
2690
2691 if len(state) == 1:
2692 self.status = state
2693 self.level = None
2694 else:
2695 self.status = state[:1]
2696 self.level = state[1:]
2697 while self.level.startswith('0'):
2698 self.level = self.level[1:]
2699
2700 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002701 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002702 if info.status in ('R', 'C'):
2703 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002704 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002705 r[info.path] = info
2706 return r
2707 finally:
2708 p.Wait()
2709
2710 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002711 if self._bare:
2712 path = os.path.join(self._project.gitdir, HEAD)
2713 else:
2714 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002715 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002716 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002717 except IOError as e:
2718 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002719 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002720 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002721 finally:
2722 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302723 try:
2724 line = line.decode()
2725 except AttributeError:
2726 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002727 if line.startswith('ref: '):
2728 return line[5:-1]
2729 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002730
2731 def SetHead(self, ref, message=None):
2732 cmdv = []
2733 if message is not None:
2734 cmdv.extend(['-m', message])
2735 cmdv.append(HEAD)
2736 cmdv.append(ref)
2737 self.symbolic_ref(*cmdv)
2738
2739 def DetachHead(self, new, message=None):
2740 cmdv = ['--no-deref']
2741 if message is not None:
2742 cmdv.extend(['-m', message])
2743 cmdv.append(HEAD)
2744 cmdv.append(new)
2745 self.update_ref(*cmdv)
2746
2747 def UpdateRef(self, name, new, old=None,
2748 message=None,
2749 detach=False):
2750 cmdv = []
2751 if message is not None:
2752 cmdv.extend(['-m', message])
2753 if detach:
2754 cmdv.append('--no-deref')
2755 cmdv.append(name)
2756 cmdv.append(new)
2757 if old is not None:
2758 cmdv.append(old)
2759 self.update_ref(*cmdv)
2760
2761 def DeleteRef(self, name, old=None):
2762 if not old:
2763 old = self.rev_parse(name)
2764 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002765 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002766
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002767 def rev_list(self, *args, **kw):
2768 if 'format' in kw:
2769 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2770 else:
2771 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002772 cmdv.extend(args)
2773 p = GitCommand(self._project,
2774 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002775 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002776 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002777 capture_stdout=True,
2778 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002779 r = []
2780 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002781 if line[-1] == '\n':
2782 line = line[:-1]
2783 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002784 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002785 raise GitError('%s rev-list %s: %s' %
2786 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002787 return r
2788
2789 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002790 """Allow arbitrary git commands using pythonic syntax.
2791
2792 This allows you to do things like:
2793 git_obj.rev_parse('HEAD')
2794
2795 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2796 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002797 Any other positional arguments will be passed to the git command, and the
2798 following keyword arguments are supported:
2799 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002800
2801 Args:
2802 name: The name of the git command to call. Any '_' characters will
2803 be replaced with '-'.
2804
2805 Returns:
2806 A callable object that will try to call git with the named command.
2807 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002808 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002809
Dave Borowitz091f8932012-10-23 17:01:04 -07002810 def runner(*args, **kwargs):
2811 cmdv = []
2812 config = kwargs.pop('config', None)
2813 for k in kwargs:
2814 raise TypeError('%s() got an unexpected keyword argument %r'
2815 % (name, k))
2816 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002817 if not git_require((1, 7, 2)):
2818 raise ValueError('cannot set config on command line for %s()'
2819 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302820 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002821 cmdv.append('-c')
2822 cmdv.append('%s=%s' % (k, v))
2823 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002824 cmdv.extend(args)
2825 p = GitCommand(self._project,
2826 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002827 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002828 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002829 capture_stdout=True,
2830 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002831 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002832 raise GitError('%s %s: %s' %
2833 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302835 try:
Conley Owensedd01512013-09-26 12:59:58 -07002836 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302837 except AttributeError:
2838 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002839 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2840 return r[:-1]
2841 return r
2842 return runner
2843
2844
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002845class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002846
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002847 def __str__(self):
2848 return 'prior sync failed; rebase still in progress'
2849
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002850
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002851class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002852
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002853 def __str__(self):
2854 return 'contains uncommitted changes'
2855
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002856
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002857class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002858
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002859 def __init__(self, project, text):
2860 self.project = project
2861 self.text = text
2862
2863 def Print(self, syncbuf):
2864 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2865 syncbuf.out.nl()
2866
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002867
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002868class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002869
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002870 def __init__(self, project, why):
2871 self.project = project
2872 self.why = why
2873
2874 def Print(self, syncbuf):
2875 syncbuf.out.fail('error: %s/: %s',
2876 self.project.relpath,
2877 str(self.why))
2878 syncbuf.out.nl()
2879
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002880
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002881class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002882
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002883 def __init__(self, project, action):
2884 self.project = project
2885 self.action = action
2886
2887 def Run(self, syncbuf):
2888 out = syncbuf.out
2889 out.project('project %s/', self.project.relpath)
2890 out.nl()
2891 try:
2892 self.action()
2893 out.nl()
2894 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002895 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002896 out.nl()
2897 return False
2898
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002899
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002900class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002901
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002902 def __init__(self, config):
2903 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002904 self.project = self.printer('header', attr='bold')
2905 self.info = self.printer('info')
2906 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002907
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002908
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002909class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002910
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002911 def __init__(self, config, detach_head=False):
2912 self._messages = []
2913 self._failures = []
2914 self._later_queue1 = []
2915 self._later_queue2 = []
2916
2917 self.out = _SyncColoring(config)
2918 self.out.redirect(sys.stderr)
2919
2920 self.detach_head = detach_head
2921 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002922 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002923
2924 def info(self, project, fmt, *args):
2925 self._messages.append(_InfoMessage(project, fmt % args))
2926
2927 def fail(self, project, err=None):
2928 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002929 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002930
2931 def later1(self, project, what):
2932 self._later_queue1.append(_Later(project, what))
2933
2934 def later2(self, project, what):
2935 self._later_queue2.append(_Later(project, what))
2936
2937 def Finish(self):
2938 self._PrintMessages()
2939 self._RunLater()
2940 self._PrintMessages()
2941 return self.clean
2942
David Rileye0684ad2017-04-05 00:02:59 -07002943 def Recently(self):
2944 recent_clean = self.recent_clean
2945 self.recent_clean = True
2946 return recent_clean
2947
2948 def _MarkUnclean(self):
2949 self.clean = False
2950 self.recent_clean = False
2951
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002952 def _RunLater(self):
2953 for q in ['_later_queue1', '_later_queue2']:
2954 if not self._RunQueue(q):
2955 return
2956
2957 def _RunQueue(self, queue):
2958 for m in getattr(self, queue):
2959 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002960 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002961 return False
2962 setattr(self, queue, [])
2963 return True
2964
2965 def _PrintMessages(self):
2966 for m in self._messages:
2967 m.Print(self)
2968 for m in self._failures:
2969 m.Print(self)
2970
2971 self._messages = []
2972 self._failures = []
2973
2974
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002975class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002976
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002977 """A special project housed under .repo.
2978 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002979
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002980 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002981 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002982 manifest=manifest,
2983 name=name,
2984 gitdir=gitdir,
2985 objdir=gitdir,
2986 worktree=worktree,
2987 remote=RemoteSpec('origin'),
2988 relpath='.repo/%s' % name,
2989 revisionExpr='refs/heads/master',
2990 revisionId=None,
2991 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002992
2993 def PreSync(self):
2994 if self.Exists:
2995 cb = self.CurrentBranch
2996 if cb:
2997 base = self.GetBranch(cb).merge
2998 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002999 self.revisionExpr = base
3000 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003001
Martin Kelly224a31a2017-07-10 14:46:25 -07003002 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003003 """ Prepare MetaProject for manifest branch switch
3004 """
3005
3006 # detach and delete manifest branch, allowing a new
3007 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003008 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003009 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003010 syncbuf.Finish()
3011
3012 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003013 ['update-ref', '-d', 'refs/heads/default'],
3014 capture_stdout=True,
3015 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003016
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003017 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003018 def LastFetch(self):
3019 try:
3020 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3021 return os.path.getmtime(fh)
3022 except OSError:
3023 return 0
3024
3025 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003026 def HasChanges(self):
3027 """Has the remote received new commits not yet checked out?
3028 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003029 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003030 return False
3031
David Pursehouse8a68ff92012-09-24 12:15:13 +09003032 all_refs = self.bare_ref.all
3033 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003034 head = self.work_git.GetHead()
3035 if head.startswith(R_HEADS):
3036 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003037 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003038 except KeyError:
3039 head = None
3040
3041 if revid == head:
3042 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003043 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003044 return True
3045 return False