blob: 704680fdf920a66335c8dc1c13672d921224a541 [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
Chirayu Desai217ea7d2013-03-01 19:14:38 +053051 input = raw_input
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070053
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070054def _lwrite(path, content):
55 lock = '%s.lock' % path
56
Chirayu Desai303a82f2014-08-19 22:57:17 +053057 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070058 try:
59 fd.write(content)
60 finally:
61 fd.close()
62
63 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070064 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070065 except OSError:
Renaud Paquay010fed72016-11-11 14:25:29 -080066 platform_utils.remove(lock)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 raise
68
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070069
Shawn O. Pearce48244782009-04-16 08:25:57 -070070def _error(fmt, *args):
71 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070072 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070073
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070074
David Pursehousef33929d2015-08-24 14:39:14 +090075def _warn(fmt, *args):
76 msg = fmt % args
77 print('warn: %s' % msg, file=sys.stderr)
78
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070079
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070080def not_rev(r):
81 return '^' + r
82
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070083
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080084def sq(r):
85 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080086
Jonathan Nieder93719792015-03-17 11:29:58 -070087_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070088
89
Jonathan Nieder93719792015-03-17 11:29:58 -070090def _ProjectHooks():
91 """List the hooks present in the 'hooks' directory.
92
93 These hooks are project hooks and are copied to the '.git/hooks' directory
94 of all subprojects.
95
96 This function caches the list of hooks (based on the contents of the
97 'repo/hooks' directory) on the first call.
98
99 Returns:
100 A list of absolute paths to all of the files in the hooks directory.
101 """
102 global _project_hook_list
103 if _project_hook_list is None:
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700104 d = platform_utils.realpath(os.path.abspath(os.path.dirname(__file__)))
Jonathan Nieder93719792015-03-17 11:29:58 -0700105 d = os.path.join(d, 'hooks')
Renaud Paquaybed8b622018-09-27 10:46:58 -0700106 _project_hook_list = [os.path.join(d, x) for x in platform_utils.listdir(d)]
Jonathan Nieder93719792015-03-17 11:29:58 -0700107 return _project_hook_list
108
109
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700110class DownloadedChange(object):
111 _commit_cache = None
112
113 def __init__(self, project, base, change_id, ps_id, commit):
114 self.project = project
115 self.base = base
116 self.change_id = change_id
117 self.ps_id = ps_id
118 self.commit = commit
119
120 @property
121 def commits(self):
122 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700123 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
124 '--abbrev-commit',
125 '--pretty=oneline',
126 '--reverse',
127 '--date-order',
128 not_rev(self.base),
129 self.commit,
130 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700131 return self._commit_cache
132
133
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700134class ReviewableBranch(object):
135 _commit_cache = None
136
137 def __init__(self, project, branch, base):
138 self.project = project
139 self.branch = branch
140 self.base = base
141
142 @property
143 def name(self):
144 return self.branch.name
145
146 @property
147 def commits(self):
148 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700149 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
150 '--abbrev-commit',
151 '--pretty=oneline',
152 '--reverse',
153 '--date-order',
154 not_rev(self.base),
155 R_HEADS + self.name,
156 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700157 return self._commit_cache
158
159 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800160 def unabbrev_commits(self):
161 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700162 for commit in self.project.bare_git.rev_list(not_rev(self.base),
163 R_HEADS + self.name,
164 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800165 r[commit[0:8]] = commit
166 return r
167
168 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700169 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700170 return self.project.bare_git.log('--pretty=format:%cd',
171 '-n', '1',
172 R_HEADS + self.name,
173 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700174
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700175 def UploadForReview(self, people,
176 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000177 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200178 private=False,
179 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200180 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800181 validate_certs=True,
182 push_options=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800183 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700184 people,
Brian Harring435370c2012-07-28 15:37:04 -0700185 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000186 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200187 private=private,
188 wip=wip,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +0200189 dest_branch=dest_branch,
Masaya Suzuki305a2d02017-11-13 10:48:34 -0800190 validate_certs=validate_certs,
191 push_options=push_options)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700192
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700193 def GetPublishedRefs(self):
194 refs = {}
195 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700196 self.branch.remote.SshReviewUrl(self.project.UserEmail),
197 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700198 for line in output.split('\n'):
199 try:
200 (sha, ref) = line.split()
201 refs[sha] = ref
202 except ValueError:
203 pass
204
205 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700206
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700207
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700209
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210 def __init__(self, config):
211 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100212 self.project = self.printer('header', attr='bold')
213 self.branch = self.printer('header', attr='bold')
214 self.nobranch = self.printer('nobranch', fg='red')
215 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Anthony King7bdac712014-07-16 12:56:40 +0100217 self.added = self.printer('added', fg='green')
218 self.changed = self.printer('changed', fg='red')
219 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700220
221
222class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700223
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700224 def __init__(self, config):
225 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100226 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
Anthony King7bdac712014-07-16 12:56:40 +0100229class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700230
James W. Mills24c13082012-04-12 15:04:13 -0500231 def __init__(self, name, value, keep):
232 self.name = name
233 self.value = value
234 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
Anthony King7bdac712014-07-16 12:56:40 +0100237class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700238
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800239 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240 self.src = src
241 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242 self.abs_src = abssrc
243 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244
245 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800246 src = self.abs_src
247 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248 # copy file if it does not exist or is out of date
249 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
250 try:
251 # remove existing file first, since it might be read-only
252 if os.path.exists(dest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800253 platform_utils.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400254 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200255 dest_dir = os.path.dirname(dest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700256 if not platform_utils.isdir(dest_dir):
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200257 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700258 shutil.copy(src, dest)
259 # make the file read-only
260 mode = os.stat(dest)[stat.ST_MODE]
261 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
262 os.chmod(dest, mode)
263 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700264 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700265
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700266
Anthony King7bdac712014-07-16 12:56:40 +0100267class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700268
Wink Saville4c426ef2015-06-03 08:05:17 -0700269 def __init__(self, git_worktree, src, dest, relsrc, absdest):
270 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500271 self.src = src
272 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700273 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 self.abs_dest = absdest
275
Wink Saville4c426ef2015-06-03 08:05:17 -0700276 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 # link file if it does not exist or is out of date
Renaud Paquay227ad2e2016-11-01 14:37:13 -0700278 if not platform_utils.islink(absDest) or (platform_utils.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 try:
280 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800281 if os.path.lexists(absDest):
Renaud Paquay010fed72016-11-11 14:25:29 -0800282 platform_utils.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700284 dest_dir = os.path.dirname(absDest)
Renaud Paquaybed8b622018-09-27 10:46:58 -0700285 if not platform_utils.isdir(dest_dir):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700287 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700289 _error('Cannot link file %s to %s', relSrc, absDest)
290
291 def _Link(self):
292 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
293 on the src linking all of the files in the source in to the destination
294 directory.
295 """
296 # We use the absSrc to handle the situation where the current directory
297 # is not the root of the repo
298 absSrc = os.path.join(self.git_worktree, self.src)
299 if os.path.exists(absSrc):
300 # Entity exists so just a simple one to one link operation
301 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
302 else:
303 # Entity doesn't exist assume there is a wild card
304 absDestDir = self.abs_dest
Renaud Paquaybed8b622018-09-27 10:46:58 -0700305 if os.path.exists(absDestDir) and not platform_utils.isdir(absDestDir):
Wink Saville4c426ef2015-06-03 08:05:17 -0700306 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700307 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700308 else:
309 absSrcFiles = glob.glob(absSrc)
310 for absSrcFile in absSrcFiles:
311 # Create a releative path from source dir to destination dir
312 absSrcDir = os.path.dirname(absSrcFile)
313 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
314
315 # Get the source file name
316 srcFile = os.path.basename(absSrcFile)
317
318 # Now form the final full paths to srcFile. They will be
319 # absolute for the desintaiton and relative for the srouce.
320 absDest = os.path.join(absDestDir, srcFile)
321 relSrc = os.path.join(relSrcDir, srcFile)
322 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500323
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700324
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700325class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700326
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327 def __init__(self,
328 name,
Anthony King7bdac712014-07-16 12:56:40 +0100329 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700330 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100331 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700332 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700333 orig_name=None,
334 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700335 self.name = name
336 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700337 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700338 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100339 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700340 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700341 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700342
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700343
Doug Anderson37282b42011-03-04 11:54:18 -0800344class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700345
Doug Anderson37282b42011-03-04 11:54:18 -0800346 """A RepoHook contains information about a script to run as a hook.
347
348 Hooks are used to run a python script before running an upload (for instance,
349 to run presubmit checks). Eventually, we may have hooks for other actions.
350
351 This shouldn't be confused with files in the 'repo/hooks' directory. Those
352 files are copied into each '.git/hooks' folder for each project. Repo-level
353 hooks are associated instead with repo actions.
354
355 Hooks are always python. When a hook is run, we will load the hook into the
356 interpreter and execute its main() function.
357 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700358
Doug Anderson37282b42011-03-04 11:54:18 -0800359 def __init__(self,
360 hook_type,
361 hooks_project,
362 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400363 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800364 abort_if_user_denies=False):
365 """RepoHook constructor.
366
367 Params:
368 hook_type: A string representing the type of hook. This is also used
369 to figure out the name of the file containing the hook. For
370 example: 'pre-upload'.
371 hooks_project: The project containing the repo hooks. If you have a
372 manifest, this is manifest.repo_hooks_project. OK if this is None,
373 which will make the hook a no-op.
374 topdir: Repo's top directory (the one containing the .repo directory).
375 Scripts will run with CWD as this directory. If you have a manifest,
376 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400377 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800378 abort_if_user_denies: If True, we'll throw a HookError() if the user
379 doesn't allow us to run the hook.
380 """
381 self._hook_type = hook_type
382 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400383 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800384 self._topdir = topdir
385 self._abort_if_user_denies = abort_if_user_denies
386
387 # Store the full path to the script for convenience.
388 if self._hooks_project:
389 self._script_fullpath = os.path.join(self._hooks_project.worktree,
390 self._hook_type + '.py')
391 else:
392 self._script_fullpath = None
393
394 def _GetHash(self):
395 """Return a hash of the contents of the hooks directory.
396
397 We'll just use git to do this. This hash has the property that if anything
398 changes in the directory we will return a different has.
399
400 SECURITY CONSIDERATION:
401 This hash only represents the contents of files in the hook directory, not
402 any other files imported or called by hooks. Changes to imported files
403 can change the script behavior without affecting the hash.
404
405 Returns:
406 A string representing the hash. This will always be ASCII so that it can
407 be printed to the user easily.
408 """
409 assert self._hooks_project, "Must have hooks to calculate their hash."
410
411 # We will use the work_git object rather than just calling GetRevisionId().
412 # That gives us a hash of the latest checked in version of the files that
413 # the user will actually be executing. Specifically, GetRevisionId()
414 # doesn't appear to change even if a user checks out a different version
415 # of the hooks repo (via git checkout) nor if a user commits their own revs.
416 #
417 # NOTE: Local (non-committed) changes will not be factored into this hash.
418 # I think this is OK, since we're really only worried about warning the user
419 # about upstream changes.
420 return self._hooks_project.work_git.rev_parse('HEAD')
421
422 def _GetMustVerb(self):
423 """Return 'must' if the hook is required; 'should' if not."""
424 if self._abort_if_user_denies:
425 return 'must'
426 else:
427 return 'should'
428
429 def _CheckForHookApproval(self):
430 """Check to see whether this hook has been approved.
431
Mike Frysinger40252c22016-08-15 21:23:44 -0400432 We'll accept approval of manifest URLs if they're using secure transports.
433 This way the user can say they trust the manifest hoster. For insecure
434 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800435
436 Note that we ask permission for each individual hook even though we use
437 the hash of all hooks when detecting changes. We'd like the user to be
438 able to approve / deny each hook individually. We only use the hash of all
439 hooks because there is no other easy way to detect changes to local imports.
440
441 Returns:
442 True if this hook is approved to run; False otherwise.
443
444 Raises:
445 HookError: Raised if the user doesn't approve and abort_if_user_denies
446 was passed to the consturctor.
447 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400448 if self._ManifestUrlHasSecureScheme():
449 return self._CheckForHookApprovalManifest()
450 else:
451 return self._CheckForHookApprovalHash()
452
453 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
454 changed_prompt):
455 """Check for approval for a particular attribute and hook.
456
457 Args:
458 subkey: The git config key under [repo.hooks.<hook_type>] to store the
459 last approved string.
460 new_val: The new value to compare against the last approved one.
461 main_prompt: Message to display to the user to ask for approval.
462 changed_prompt: Message explaining why we're re-asking for approval.
463
464 Returns:
465 True if this hook is approved to run; False otherwise.
466
467 Raises:
468 HookError: Raised if the user doesn't approve and abort_if_user_denies
469 was passed to the consturctor.
470 """
Doug Anderson37282b42011-03-04 11:54:18 -0800471 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400472 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800473
Mike Frysinger40252c22016-08-15 21:23:44 -0400474 # Get the last value that the user approved for this hook; may be None.
475 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800476
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800478 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400479 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800480 # Approval matched. We're done.
481 return True
482 else:
483 # Give the user a reason why we're prompting, since they last told
484 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400485 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800486 else:
487 prompt = ''
488
489 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
490 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400491 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530492 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900493 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800494
495 # User is doing a one-time approval.
496 if response in ('y', 'yes'):
497 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400498 elif response == 'always':
499 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800500 return True
501
502 # For anything else, we'll assume no approval.
503 if self._abort_if_user_denies:
504 raise HookError('You must allow the %s hook or use --no-verify.' %
505 self._hook_type)
506
507 return False
508
Mike Frysinger40252c22016-08-15 21:23:44 -0400509 def _ManifestUrlHasSecureScheme(self):
510 """Check if the URI for the manifest is a secure transport."""
511 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
512 parse_results = urllib.parse.urlparse(self._manifest_url)
513 return parse_results.scheme in secure_schemes
514
515 def _CheckForHookApprovalManifest(self):
516 """Check whether the user has approved this manifest host.
517
518 Returns:
519 True if this hook is approved to run; False otherwise.
520 """
521 return self._CheckForHookApprovalHelper(
522 'approvedmanifest',
523 self._manifest_url,
524 'Run hook scripts from %s' % (self._manifest_url,),
525 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
526
527 def _CheckForHookApprovalHash(self):
528 """Check whether the user has approved the hooks repo.
529
530 Returns:
531 True if this hook is approved to run; False otherwise.
532 """
533 prompt = ('Repo %s run the script:\n'
534 ' %s\n'
535 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700536 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400537 return self._CheckForHookApprovalHelper(
538 'approvedhash',
539 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700540 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400541 'Scripts have changed since %s was allowed.' % (self._hook_type,))
542
Doug Anderson37282b42011-03-04 11:54:18 -0800543 def _ExecuteHook(self, **kwargs):
544 """Actually execute the given hook.
545
546 This will run the hook's 'main' function in our python interpreter.
547
548 Args:
549 kwargs: Keyword arguments to pass to the hook. These are often specific
550 to the hook type. For instance, pre-upload hooks will contain
551 a project_list.
552 """
553 # Keep sys.path and CWD stashed away so that we can always restore them
554 # upon function exit.
555 orig_path = os.getcwd()
556 orig_syspath = sys.path
557
558 try:
559 # Always run hooks with CWD as topdir.
560 os.chdir(self._topdir)
561
562 # Put the hook dir as the first item of sys.path so hooks can do
563 # relative imports. We want to replace the repo dir as [0] so
564 # hooks can't import repo files.
565 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
566
567 # Exec, storing global context in the context dict. We catch exceptions
568 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500569 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800570 try:
Anthony King70f68902014-05-05 21:15:34 +0100571 exec(compile(open(self._script_fullpath).read(),
572 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800573 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700574 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
575 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800576
577 # Running the script should have defined a main() function.
578 if 'main' not in context:
579 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
580
Doug Anderson37282b42011-03-04 11:54:18 -0800581 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
582 # We don't actually want hooks to define their main with this argument--
583 # it's there to remind them that their hook should always take **kwargs.
584 # For instance, a pre-upload hook should be defined like:
585 # def main(project_list, **kwargs):
586 #
587 # This allows us to later expand the API without breaking old hooks.
588 kwargs = kwargs.copy()
589 kwargs['hook_should_take_kwargs'] = True
590
591 # Call the main function in the hook. If the hook should cause the
592 # build to fail, it will raise an Exception. We'll catch that convert
593 # to a HookError w/ just the failing traceback.
594 try:
595 context['main'](**kwargs)
596 except Exception:
597 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700598 'above.' % (traceback.format_exc(),
599 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800600 finally:
601 # Restore sys.path and CWD.
602 sys.path = orig_syspath
603 os.chdir(orig_path)
604
605 def Run(self, user_allows_all_hooks, **kwargs):
606 """Run the hook.
607
608 If the hook doesn't exist (because there is no hooks project or because
609 this particular hook is not enabled), this is a no-op.
610
611 Args:
612 user_allows_all_hooks: If True, we will never prompt about running the
613 hook--we'll just assume it's OK to run it.
614 kwargs: Keyword arguments to pass to the hook. These are often specific
615 to the hook type. For instance, pre-upload hooks will contain
616 a project_list.
617
618 Raises:
619 HookError: If there was a problem finding the hook or the user declined
620 to run a required hook (from _CheckForHookApproval).
621 """
622 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700623 if ((not self._hooks_project) or (self._hook_type not in
624 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800625 return
626
627 # Bail with a nice error if we can't find the hook.
628 if not os.path.isfile(self._script_fullpath):
629 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
630
631 # Make sure the user is OK with running the hook.
632 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
633 return
634
635 # Run the hook with the same version of python we're using.
636 self._ExecuteHook(**kwargs)
637
638
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700639class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600640 # These objects can be shared between several working trees.
641 shareable_files = ['description', 'info']
642 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
643 # These objects can only be used by a single working tree.
644 working_tree_files = ['config', 'packed-refs', 'shallow']
645 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700646
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700647 def __init__(self,
648 manifest,
649 name,
650 remote,
651 gitdir,
David James8d201162013-10-11 17:03:19 -0700652 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700653 worktree,
654 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700655 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800656 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100657 rebase=True,
658 groups=None,
659 sync_c=False,
660 sync_s=False,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900661 sync_tags=True,
Anthony King7bdac712014-07-16 12:56:40 +0100662 clone_depth=None,
663 upstream=None,
664 parent=None,
665 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900666 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700667 optimized_fetch=False,
668 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800669 """Init a Project object.
670
671 Args:
672 manifest: The XmlManifest object.
673 name: The `name` attribute of manifest.xml's project element.
674 remote: RemoteSpec object specifying its remote's properties.
675 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700676 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800677 worktree: Absolute path of git working tree.
678 relpath: Relative path of git working tree to repo's top directory.
679 revisionExpr: The `revision` attribute of manifest.xml's project element.
680 revisionId: git commit id for checking out.
681 rebase: The `rebase` attribute of manifest.xml's project element.
682 groups: The `groups` attribute of manifest.xml's project element.
683 sync_c: The `sync-c` attribute of manifest.xml's project element.
684 sync_s: The `sync-s` attribute of manifest.xml's project element.
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900685 sync_tags: The `sync-tags` attribute of manifest.xml's project element.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800686 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
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +0900718 self.sync_tags = sync_tags
David Pursehouseede7f122012-11-27 22:25:30 +0900719 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700720 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800721 self.parent = parent
722 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900723 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800724 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800725
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700726 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500728 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500729 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700730 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
731 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700732
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800733 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700734 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800735 else:
736 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700737 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700738 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700739 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400740 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700741 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742
Doug Anderson37282b42011-03-04 11:54:18 -0800743 # This will be filled in if a project is later identified to be the
744 # project containing repo hooks.
745 self.enabled_repo_hooks = []
746
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800748 def Derived(self):
749 return self.is_derived
750
751 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700752 def Exists(self):
Renaud Paquaybed8b622018-09-27 10:46:58 -0700753 return platform_utils.isdir(self.gitdir) and platform_utils.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754
755 @property
756 def CurrentBranch(self):
757 """Obtain the name of the currently checked out branch.
758 The branch name omits the 'refs/heads/' prefix.
759 None is returned if the project is on a detached HEAD.
760 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700761 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700762 if b.startswith(R_HEADS):
763 return b[len(R_HEADS):]
764 return None
765
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700766 def IsRebaseInProgress(self):
767 w = self.worktree
768 g = os.path.join(w, '.git')
769 return os.path.exists(os.path.join(g, 'rebase-apply')) \
770 or os.path.exists(os.path.join(g, 'rebase-merge')) \
771 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200772
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773 def IsDirty(self, consider_untracked=True):
774 """Is the working directory modified in some way?
775 """
776 self.work_git.update_index('-q',
777 '--unmerged',
778 '--ignore-missing',
779 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900780 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700781 return True
782 if self.work_git.DiffZ('diff-files'):
783 return True
784 if consider_untracked and self.work_git.LsOthers():
785 return True
786 return False
787
788 _userident_name = None
789 _userident_email = None
790
791 @property
792 def UserName(self):
793 """Obtain the user's personal name.
794 """
795 if self._userident_name is None:
796 self._LoadUserIdentity()
797 return self._userident_name
798
799 @property
800 def UserEmail(self):
801 """Obtain the user's email address. This is very likely
802 to be their Gerrit login.
803 """
804 if self._userident_email is None:
805 self._LoadUserIdentity()
806 return self._userident_email
807
808 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900809 u = self.bare_git.var('GIT_COMMITTER_IDENT')
810 m = re.compile("^(.*) <([^>]*)> ").match(u)
811 if m:
812 self._userident_name = m.group(1)
813 self._userident_email = m.group(2)
814 else:
815 self._userident_name = ''
816 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700817
818 def GetRemote(self, name):
819 """Get the configuration for a single remote.
820 """
821 return self.config.GetRemote(name)
822
823 def GetBranch(self, name):
824 """Get the configuration for a single branch.
825 """
826 return self.config.GetBranch(name)
827
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 def GetBranches(self):
829 """Get all existing local branches.
830 """
831 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900832 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700833 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700834
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530835 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700836 if name.startswith(R_HEADS):
837 name = name[len(R_HEADS):]
838 b = self.GetBranch(name)
839 b.current = name == current
840 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900841 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700842 heads[name] = b
843
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530844 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845 if name.startswith(R_PUB):
846 name = name[len(R_PUB):]
847 b = heads.get(name)
848 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900849 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700850
851 return heads
852
Colin Cross5acde752012-03-28 20:15:45 -0700853 def MatchesGroups(self, manifest_groups):
854 """Returns true if the manifest groups specified at init should cause
855 this project to be synced.
856 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700857 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700858
859 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700861 manifest_groups: "-group1,group2"
862 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500863
864 The special manifest group "default" will match any project that
865 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700866 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500867 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700869 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500870 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871
Conley Owens971de8e2012-04-16 10:36:08 -0700872 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700873 for group in expanded_manifest_groups:
874 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700875 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700876 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700877 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700878
Conley Owens971de8e2012-04-16 10:36:08 -0700879 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700880
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700881# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700882 def UncommitedFiles(self, get_all=True):
883 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700884
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 Args:
886 get_all: a boolean, if True - get information about all different
887 uncommitted files. If False - return as soon as any kind of
888 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500889 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700890 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500891 self.work_git.update_index('-q',
892 '--unmerged',
893 '--ignore-missing',
894 '--refresh')
895 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700896 details.append("rebase in progress")
897 if not get_all:
898 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500899
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700900 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
901 if changes:
902 details.extend(changes)
903 if not get_all:
904 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500905
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700906 changes = self.work_git.DiffZ('diff-files').keys()
907 if changes:
908 details.extend(changes)
909 if not get_all:
910 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500911
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700912 changes = self.work_git.LsOthers()
913 if changes:
914 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500915
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700916 return details
917
918 def HasChanges(self):
919 """Returns true if there are uncommitted changes.
920 """
921 if self.UncommitedFiles(get_all=False):
922 return True
923 else:
924 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500925
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600926 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700927 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200928
929 Args:
930 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600931 quiet: If True then only print the project name. Do not print
932 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700933 """
Renaud Paquaybed8b622018-09-27 10:46:58 -0700934 if not platform_utils.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700935 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200936 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700937 print(file=output_redir)
938 print('project %s/' % self.relpath, file=output_redir)
939 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700940 return
941
942 self.work_git.update_index('-q',
943 '--unmerged',
944 '--ignore-missing',
945 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700946 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
948 df = self.work_git.DiffZ('diff-files')
949 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100950 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700951 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
953 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700954 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200955 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700956 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600958 if quiet:
959 out.nl()
960 return 'DIRTY'
961
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700962 branch = self.CurrentBranch
963 if branch is None:
964 out.nobranch('(*** NO BRANCH ***)')
965 else:
966 out.branch('branch %s', branch)
967 out.nl()
968
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700969 if rb:
970 out.important('prior sync failed; rebase still in progress')
971 out.nl()
972
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700973 paths = list()
974 paths.extend(di.keys())
975 paths.extend(df.keys())
976 paths.extend(do)
977
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530978 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900979 try:
980 i = di[p]
981 except KeyError:
982 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700983
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 try:
985 f = df[p]
986 except KeyError:
987 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 if i:
990 i_status = i.status.upper()
991 else:
992 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900994 if f:
995 f_status = f.status.lower()
996 else:
997 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700998
999 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -08001000 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001001 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001002 else:
1003 line = ' %s%s\t%s' % (i_status, f_status, p)
1004
1005 if i and not f:
1006 out.added('%s', line)
1007 elif (i and f) or (not i and f):
1008 out.changed('%s', line)
1009 elif not i and not f:
1010 out.untracked('%s', line)
1011 else:
1012 out.write('%s', line)
1013 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001014
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001015 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016
pelyad67872d2012-03-28 14:49:58 +03001017 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001018 """Prints the status of the repository to stdout.
1019 """
1020 out = DiffColoring(self.config)
1021 cmd = ['diff']
1022 if out.is_on:
1023 cmd.append('--color')
1024 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001025 if absolute_paths:
1026 cmd.append('--src-prefix=a/%s/' % self.relpath)
1027 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028 cmd.append('--')
1029 p = GitCommand(self,
1030 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001031 capture_stdout=True,
1032 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 has_diff = False
1034 for line in p.process.stdout:
1035 if not has_diff:
1036 out.nl()
1037 out.project('project %s/' % self.relpath)
1038 out.nl()
1039 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001040 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001041 p.Wait()
1042
1043
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001044# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001045
David Pursehouse8a68ff92012-09-24 12:15:13 +09001046 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001047 """Was the branch published (uploaded) for code review?
1048 If so, returns the SHA-1 hash of the last published
1049 state for the branch.
1050 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001051 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001052 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001053 try:
1054 return self.bare_git.rev_parse(key)
1055 except GitError:
1056 return None
1057 else:
1058 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001059 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001060 except KeyError:
1061 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001062
David Pursehouse8a68ff92012-09-24 12:15:13 +09001063 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001064 """Prunes any stale published refs.
1065 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 if all_refs is None:
1067 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068 heads = set()
1069 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301070 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001071 if name.startswith(R_HEADS):
1072 heads.add(name)
1073 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001074 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301076 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 n = name[len(R_PUB):]
1078 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001079 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001081 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 """List any branches which can be uploaded for review.
1083 """
1084 heads = {}
1085 pubed = {}
1086
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301087 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001089 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001090 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001091 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001092
1093 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301094 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001095 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001096 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001097 if selected_branch and branch != selected_branch:
1098 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001099
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001100 rb = self.GetUploadableBranch(branch)
1101 if rb:
1102 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001103 return ready
1104
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001105 def GetUploadableBranch(self, branch_name):
1106 """Get a single uploadable branch, or None.
1107 """
1108 branch = self.GetBranch(branch_name)
1109 base = branch.LocalMerge
1110 if branch.LocalMerge:
1111 rb = ReviewableBranch(self, branch, base)
1112 if rb.commits:
1113 return rb
1114 return None
1115
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001116 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001117 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001118 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001119 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001120 private=False,
1121 wip=False,
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001122 dest_branch=None,
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001123 validate_certs=True,
1124 push_options=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001125 """Uploads the named branch for code review.
1126 """
1127 if branch is None:
1128 branch = self.CurrentBranch
1129 if branch is None:
1130 raise GitError('not currently on a branch')
1131
1132 branch = self.GetBranch(branch)
1133 if not branch.LocalMerge:
1134 raise GitError('branch %s does not track a remote' % branch.name)
1135 if not branch.remote.review:
1136 raise GitError('remote %s has no review url' % branch.remote.name)
1137
Bryan Jacobsf609f912013-05-06 13:36:24 -04001138 if dest_branch is None:
1139 dest_branch = self.dest_branch
1140 if dest_branch is None:
1141 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001142 if not dest_branch.startswith(R_HEADS):
1143 dest_branch = R_HEADS + dest_branch
1144
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001145 if not branch.remote.projectname:
1146 branch.remote.projectname = self.name
1147 branch.remote.Save()
1148
Łukasz Gardońbed59ce2017-08-08 10:18:11 +02001149 url = branch.remote.ReviewUrl(self.UserEmail, validate_certs)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001150 if url is None:
1151 raise UploadError('review not configured')
1152 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001153
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001154 if url.startswith('ssh://'):
Jonathan Nieder713c5872018-11-05 13:21:52 -08001155 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001156
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001157 for push_option in (push_options or []):
1158 cmd.append('-o')
1159 cmd.append(push_option)
1160
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001161 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001162
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001163 if dest_branch.startswith(R_HEADS):
1164 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001165
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001166 upload_type = 'for'
1167 if draft:
1168 upload_type = 'drafts'
1169
1170 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1171 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001172 if auto_topic:
1173 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001174
Jonathan Nieder713c5872018-11-05 13:21:52 -08001175 opts = ['r=%s' % p for p in people[0]]
1176 opts += ['cc=%s' % p for p in people[1]]
1177 if private:
1178 opts += ['private']
1179 if wip:
1180 opts += ['wip']
1181 if opts:
1182 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001183 cmd.append(ref_spec)
1184
Anthony King7bdac712014-07-16 12:56:40 +01001185 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001186 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
1188 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1189 self.bare_git.UpdateRef(R_PUB + branch.name,
1190 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001191 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001192
1193
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001194# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001195
Julien Campergue335f5ef2013-10-16 11:02:35 +02001196 def _ExtractArchive(self, tarpath, path=None):
1197 """Extract the given tar on its current location
1198
1199 Args:
1200 - tarpath: The path to the actual tar file
1201
1202 """
1203 try:
1204 with tarfile.open(tarpath, 'r') as tar:
1205 tar.extractall(path=path)
1206 return True
1207 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001208 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001209 return False
1210
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001211 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001212 quiet=False,
1213 is_new=None,
1214 current_branch_only=False,
1215 force_sync=False,
1216 clone_bundle=True,
1217 no_tags=False,
1218 archive=False,
1219 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001220 prune=False,
1221 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001222 """Perform only the network IO portion of the sync process.
1223 Local working directory/branch state is not affected.
1224 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001225 if archive and not isinstance(self, MetaProject):
1226 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001227 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001228 return False
1229
1230 name = self.relpath.replace('\\', '/')
1231 name = name.replace('/', '_')
1232 tarpath = '%s.tar' % name
1233 topdir = self.manifest.topdir
1234
1235 try:
1236 self._FetchArchive(tarpath, cwd=topdir)
1237 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001238 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001239 return False
1240
1241 # From now on, we only need absolute tarpath
1242 tarpath = os.path.join(topdir, tarpath)
1243
1244 if not self._ExtractArchive(tarpath, path=topdir):
1245 return False
1246 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001247 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001248 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001249 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001250 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001251 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001252 if is_new is None:
1253 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001254 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001255 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001256 else:
1257 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001258 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001259
1260 if is_new:
1261 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1262 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001263 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001264 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001265 # This works for both absolute and relative alternate directories.
1266 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001267 finally:
1268 fd.close()
1269 except IOError:
1270 alt_dir = None
1271 else:
1272 alt_dir = None
1273
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001274 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001275 and alt_dir is None \
1276 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001277 is_new = False
1278
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001279 if not current_branch_only:
1280 if self.sync_c:
1281 current_branch_only = True
1282 elif not self.manifest._loaded:
1283 # Manifest cannot check defaults until it syncs.
1284 current_branch_only = False
1285 elif self.manifest.default.sync_c:
1286 current_branch_only = True
1287
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001288 if not no_tags:
1289 if not self.sync_tags:
1290 no_tags = True
1291
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001292 if self.clone_depth:
1293 depth = self.clone_depth
1294 else:
1295 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1296
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001297 need_to_fetch = not (optimized_fetch and
1298 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001299 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001300 if (need_to_fetch and
1301 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1302 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001303 no_tags=no_tags, prune=prune, depth=depth,
1304 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001305 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001306
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001307 mp = self.manifest.manifestProject
1308 dissociate = mp.config.GetBoolean('repo.dissociate')
1309 if dissociate:
1310 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1311 if os.path.exists(alternates_file):
1312 cmd = ['repack', '-a', '-d']
1313 if GitCommand(self, cmd, bare=True).Wait() != 0:
1314 return False
1315 platform_utils.remove(alternates_file)
1316
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001317 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001318 self._InitMRef()
1319 else:
1320 self._InitMirrorHead()
1321 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001322 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001323 except OSError:
1324 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001325 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001326
1327 def PostRepoUpgrade(self):
1328 self._InitHooks()
1329
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001330 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001331 if self.manifest.isGitcClient:
1332 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001333 for copyfile in self.copyfiles:
1334 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001335 for linkfile in self.linkfiles:
1336 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001337
Julien Camperguedd654222014-01-09 16:21:37 +01001338 def GetCommitRevisionId(self):
1339 """Get revisionId of a commit.
1340
1341 Use this method instead of GetRevisionId to get the id of the commit rather
1342 than the id of the current git object (for example, a tag)
1343
1344 """
1345 if not self.revisionExpr.startswith(R_TAGS):
1346 return self.GetRevisionId(self._allrefs)
1347
1348 try:
1349 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1350 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001351 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1352 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001353
David Pursehouse8a68ff92012-09-24 12:15:13 +09001354 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001355 if self.revisionId:
1356 return self.revisionId
1357
1358 rem = self.GetRemote(self.remote.name)
1359 rev = rem.ToLocal(self.revisionExpr)
1360
David Pursehouse8a68ff92012-09-24 12:15:13 +09001361 if all_refs is not None and rev in all_refs:
1362 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001363
1364 try:
1365 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1366 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001367 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1368 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001369
Martin Kellye4e94d22017-03-21 16:05:12 -07001370 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001371 """Perform only the local IO portion of the sync process.
1372 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001373 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001374 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001375 all_refs = self.bare_ref.all
1376 self.CleanPublishedCache(all_refs)
1377 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001378
David Pursehouse1d947b32012-10-25 12:23:11 +09001379 def _doff():
1380 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001381 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001382
Martin Kellye4e94d22017-03-21 16:05:12 -07001383 def _dosubmodules():
1384 self._SyncSubmodules(quiet=True)
1385
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001386 head = self.work_git.GetHead()
1387 if head.startswith(R_HEADS):
1388 branch = head[len(R_HEADS):]
1389 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001390 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001391 except KeyError:
1392 head = None
1393 else:
1394 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001395
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001396 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001397 # Currently on a detached HEAD. The user is assumed to
1398 # not have any local modifications worth worrying about.
1399 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001400 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001401 syncbuf.fail(self, _PriorSyncFailedError())
1402 return
1403
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001404 if head == revid:
1405 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001406 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001407 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001408 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001409 # The copy/linkfile config may have changed.
1410 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001411 return
1412 else:
1413 lost = self._revlist(not_rev(revid), HEAD)
1414 if lost:
1415 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001416
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001417 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001418 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001419 if submodules:
1420 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001421 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001422 syncbuf.fail(self, e)
1423 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001424 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001425 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001426
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001427 if head == revid:
1428 # No changes; don't do anything further.
1429 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001430 # The copy/linkfile config may have changed.
1431 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001432 return
1433
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001434 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001435
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001436 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001437 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001438 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001439 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001440 syncbuf.info(self,
1441 "leaving %s; does not track upstream",
1442 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001444 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001445 if submodules:
1446 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001447 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001448 syncbuf.fail(self, e)
1449 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001450 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001451 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001452
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001453 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001454 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001456 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001457 if not_merged:
1458 if upstream_gain:
1459 # The user has published this branch and some of those
1460 # commits are not yet merged upstream. We do not want
1461 # to rewrite the published commits so we punt.
1462 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001463 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001464 "branch %s is published (but not merged) and is now "
1465 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001466 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001467 elif pub == head:
1468 # All published commits are merged, and thus we are a
1469 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001470 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001471 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001472 if submodules:
1473 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001474 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001475
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001476 # Examine the local commits not in the remote. Find the
1477 # last one attributed to this user, if any.
1478 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001479 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001480 last_mine = None
1481 cnt_mine = 0
1482 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301483 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001484 if committer_email == self.UserEmail:
1485 last_mine = commit_id
1486 cnt_mine += 1
1487
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001488 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001489 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001490
1491 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001492 syncbuf.fail(self, _DirtyError())
1493 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001494
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001495 # If the upstream switched on us, warn the user.
1496 #
1497 if branch.merge != self.revisionExpr:
1498 if branch.merge and self.revisionExpr:
1499 syncbuf.info(self,
1500 'manifest switched %s...%s',
1501 branch.merge,
1502 self.revisionExpr)
1503 elif branch.merge:
1504 syncbuf.info(self,
1505 'manifest no longer tracks %s',
1506 branch.merge)
1507
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001508 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001509 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001510 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001511 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001512 syncbuf.info(self,
1513 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001514 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001516 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001517 if not ID_RE.match(self.revisionExpr):
1518 # in case of manifest sync the revisionExpr might be a SHA1
1519 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001520 if not branch.merge.startswith('refs/'):
1521 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001522 branch.Save()
1523
Mike Pontillod3153822012-02-28 11:53:24 -08001524 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001525 def _docopyandlink():
1526 self._CopyAndLinkFiles()
1527
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001528 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001529 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001530 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001531 if submodules:
1532 syncbuf.later2(self, _dosubmodules)
1533 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001534 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001535 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001536 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001537 if submodules:
1538 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001539 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001540 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001541 syncbuf.fail(self, e)
1542 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001543 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001544 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001545 if submodules:
1546 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001547
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001548 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001549 # dest should already be an absolute path, but src is project relative
1550 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001551 abssrc = os.path.join(self.worktree, src)
1552 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001554 def AddLinkFile(self, src, dest, absdest):
1555 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001556 # make src relative path to dest
1557 absdestdir = os.path.dirname(absdest)
1558 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001559 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001560
James W. Mills24c13082012-04-12 15:04:13 -05001561 def AddAnnotation(self, name, value, keep):
1562 self.annotations.append(_Annotation(name, value, keep))
1563
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001564 def DownloadPatchSet(self, change_id, patch_id):
1565 """Download a single patch set of a single change to FETCH_HEAD.
1566 """
1567 remote = self.GetRemote(self.remote.name)
1568
1569 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001570 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001571 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001572 if GitCommand(self, cmd, bare=True).Wait() != 0:
1573 return None
1574 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001575 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001576 change_id,
1577 patch_id,
1578 self.bare_git.rev_parse('FETCH_HEAD'))
1579
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001580
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001581# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582
Simran Basib9a1b732015-08-20 12:19:28 -07001583 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001584 """Create a new branch off the manifest's revision.
1585 """
Simran Basib9a1b732015-08-20 12:19:28 -07001586 if not branch_merge:
1587 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001588 head = self.work_git.GetHead()
1589 if head == (R_HEADS + name):
1590 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001591
David Pursehouse8a68ff92012-09-24 12:15:13 +09001592 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001593 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001594 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001595 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001596 capture_stdout=True,
1597 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001598
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001599 branch = self.GetBranch(name)
1600 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001601 branch.merge = branch_merge
1602 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1603 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001604 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001605
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001606 if head.startswith(R_HEADS):
1607 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001608 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001609 except KeyError:
1610 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001611 if revid and head and revid == head:
1612 ref = os.path.join(self.gitdir, R_HEADS + name)
1613 try:
1614 os.makedirs(os.path.dirname(ref))
1615 except OSError:
1616 pass
1617 _lwrite(ref, '%s\n' % revid)
1618 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1619 'ref: %s%s\n' % (R_HEADS, name))
1620 branch.Save()
1621 return True
1622
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001623 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001624 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001625 capture_stdout=True,
1626 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001627 branch.Save()
1628 return True
1629 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001630
Wink Saville02d79452009-04-10 13:01:24 -07001631 def CheckoutBranch(self, name):
1632 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001633
1634 Args:
1635 name: The name of the branch to checkout.
1636
1637 Returns:
1638 True if the checkout succeeded; False if it didn't; None if the branch
1639 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001640 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001641 rev = R_HEADS + name
1642 head = self.work_git.GetHead()
1643 if head == rev:
1644 # Already on the branch
1645 #
1646 return True
Wink Saville02d79452009-04-10 13:01:24 -07001647
David Pursehouse8a68ff92012-09-24 12:15:13 +09001648 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001649 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001650 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001651 except KeyError:
1652 # Branch does not exist in this project
1653 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001654 return None
Wink Saville02d79452009-04-10 13:01:24 -07001655
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001656 if head.startswith(R_HEADS):
1657 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001658 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001659 except KeyError:
1660 head = None
1661
1662 if head == revid:
1663 # Same revision; just update HEAD to point to the new
1664 # target branch, but otherwise take no other action.
1665 #
1666 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1667 'ref: %s%s\n' % (R_HEADS, name))
1668 return True
1669
1670 return GitCommand(self,
1671 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001672 capture_stdout=True,
1673 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001674
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001675 def AbandonBranch(self, name):
1676 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001677
1678 Args:
1679 name: The name of the branch to abandon.
1680
1681 Returns:
1682 True if the abandon succeeded; False if it didn't; None if the branch
1683 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001684 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001685 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001686 all_refs = self.bare_ref.all
1687 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001688 # Doesn't exist
1689 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001690
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001691 head = self.work_git.GetHead()
1692 if head == rev:
1693 # We can't destroy the branch while we are sitting
1694 # on it. Switch to a detached HEAD.
1695 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001696 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001697
David Pursehouse8a68ff92012-09-24 12:15:13 +09001698 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001699 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001700 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1701 '%s\n' % revid)
1702 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001703 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001704
1705 return GitCommand(self,
1706 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001707 capture_stdout=True,
1708 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001709
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001710 def PruneHeads(self):
1711 """Prune any topic branches already merged into upstream.
1712 """
1713 cb = self.CurrentBranch
1714 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001715 left = self._allrefs
1716 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001717 if name.startswith(R_HEADS):
1718 name = name[len(R_HEADS):]
1719 if cb is None or name != cb:
1720 kill.append(name)
1721
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001722 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723 if cb is not None \
1724 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001725 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001726 self.work_git.DetachHead(HEAD)
1727 kill.append(cb)
1728
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001730 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001731
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732 try:
1733 self.bare_git.DetachHead(rev)
1734
1735 b = ['branch', '-d']
1736 b.extend(kill)
1737 b = GitCommand(self, b, bare=True,
1738 capture_stdout=True,
1739 capture_stderr=True)
1740 b.Wait()
1741 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001742 if ID_RE.match(old):
1743 self.bare_git.DetachHead(old)
1744 else:
1745 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001746 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001747
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001748 for branch in kill:
1749 if (R_HEADS + branch) not in left:
1750 self.CleanPublishedCache()
1751 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001752
1753 if cb and cb not in kill:
1754 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001755 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001756
1757 kept = []
1758 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001759 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001760 branch = self.GetBranch(branch)
1761 base = branch.LocalMerge
1762 if not base:
1763 base = rev
1764 kept.append(ReviewableBranch(self, branch, base))
1765 return kept
1766
1767
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001768# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001769
1770 def GetRegisteredSubprojects(self):
1771 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001772
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001773 def rec(subprojects):
1774 if not subprojects:
1775 return
1776 result.extend(subprojects)
1777 for p in subprojects:
1778 rec(p.subprojects)
1779 rec(self.subprojects)
1780 return result
1781
1782 def _GetSubmodules(self):
1783 # Unfortunately we cannot call `git submodule status --recursive` here
1784 # because the working tree might not exist yet, and it cannot be used
1785 # without a working tree in its current implementation.
1786
1787 def get_submodules(gitdir, rev):
1788 # Parse .gitmodules for submodule sub_paths and sub_urls
1789 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1790 if not sub_paths:
1791 return []
1792 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1793 # revision of submodule repository
1794 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1795 submodules = []
1796 for sub_path, sub_url in zip(sub_paths, sub_urls):
1797 try:
1798 sub_rev = sub_revs[sub_path]
1799 except KeyError:
1800 # Ignore non-exist submodules
1801 continue
1802 submodules.append((sub_rev, sub_path, sub_url))
1803 return submodules
1804
1805 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1806 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001807
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001808 def parse_gitmodules(gitdir, rev):
1809 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1810 try:
Anthony King7bdac712014-07-16 12:56:40 +01001811 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1812 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001813 except GitError:
1814 return [], []
1815 if p.Wait() != 0:
1816 return [], []
1817
1818 gitmodules_lines = []
1819 fd, temp_gitmodules_path = tempfile.mkstemp()
1820 try:
1821 os.write(fd, p.stdout)
1822 os.close(fd)
1823 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001824 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1825 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001826 if p.Wait() != 0:
1827 return [], []
1828 gitmodules_lines = p.stdout.split('\n')
1829 except GitError:
1830 return [], []
1831 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001832 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001833
1834 names = set()
1835 paths = {}
1836 urls = {}
1837 for line in gitmodules_lines:
1838 if not line:
1839 continue
1840 m = re_path.match(line)
1841 if m:
1842 names.add(m.group(1))
1843 paths[m.group(1)] = m.group(2)
1844 continue
1845 m = re_url.match(line)
1846 if m:
1847 names.add(m.group(1))
1848 urls[m.group(1)] = m.group(2)
1849 continue
1850 names = sorted(names)
1851 return ([paths.get(name, '') for name in names],
1852 [urls.get(name, '') for name in names])
1853
1854 def git_ls_tree(gitdir, rev, paths):
1855 cmd = ['ls-tree', rev, '--']
1856 cmd.extend(paths)
1857 try:
Anthony King7bdac712014-07-16 12:56:40 +01001858 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1859 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001860 except GitError:
1861 return []
1862 if p.Wait() != 0:
1863 return []
1864 objects = {}
1865 for line in p.stdout.split('\n'):
1866 if not line.strip():
1867 continue
1868 object_rev, object_path = line.split()[2:4]
1869 objects[object_path] = object_rev
1870 return objects
1871
1872 try:
1873 rev = self.GetRevisionId()
1874 except GitError:
1875 return []
1876 return get_submodules(self.gitdir, rev)
1877
1878 def GetDerivedSubprojects(self):
1879 result = []
1880 if not self.Exists:
1881 # If git repo does not exist yet, querying its submodules will
1882 # mess up its states; so return here.
1883 return result
1884 for rev, path, url in self._GetSubmodules():
1885 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001886 relpath, worktree, gitdir, objdir = \
1887 self.manifest.GetSubprojectPaths(self, name, path)
1888 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001889 if project:
1890 result.extend(project.GetDerivedSubprojects())
1891 continue
David James8d201162013-10-11 17:03:19 -07001892
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001893 if url.startswith('..'):
1894 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001895 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001896 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001897 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001898 review=self.remote.review,
1899 revision=self.remote.revision)
1900 subproject = Project(manifest=self.manifest,
1901 name=name,
1902 remote=remote,
1903 gitdir=gitdir,
1904 objdir=objdir,
1905 worktree=worktree,
1906 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001907 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001908 revisionId=rev,
1909 rebase=self.rebase,
1910 groups=self.groups,
1911 sync_c=self.sync_c,
1912 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001913 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001914 parent=self,
1915 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001916 result.append(subproject)
1917 result.extend(subproject.GetDerivedSubprojects())
1918 return result
1919
1920
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001921# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001922 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001923 try:
1924 # if revision (sha or tag) is not present then following function
1925 # throws an error.
1926 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1927 return True
1928 except GitError:
1929 # There is no such persistent revision. We have to fetch it.
1930 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001931
Julien Campergue335f5ef2013-10-16 11:02:35 +02001932 def _FetchArchive(self, tarpath, cwd=None):
1933 cmd = ['archive', '-v', '-o', tarpath]
1934 cmd.append('--remote=%s' % self.remote.url)
1935 cmd.append('--prefix=%s/' % self.relpath)
1936 cmd.append(self.revisionExpr)
1937
1938 command = GitCommand(self, cmd, cwd=cwd,
1939 capture_stdout=True,
1940 capture_stderr=True)
1941
1942 if command.Wait() != 0:
1943 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1944
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001945 def _RemoteFetch(self, name=None,
1946 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001947 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001948 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001949 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001950 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001951 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001952 depth=None,
1953 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001954
1955 is_sha1 = False
1956 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001957 # The depth should not be used when fetching to a mirror because
1958 # it will result in a shallow repository that cannot be cloned or
1959 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001960 # The repo project should also never be synced with partial depth.
1961 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1962 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001963
Shawn Pearce69e04d82014-01-29 12:48:54 -08001964 if depth:
1965 current_branch_only = True
1966
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001967 if ID_RE.match(self.revisionExpr) is not None:
1968 is_sha1 = True
1969
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001970 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001971 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001972 # this is a tag and its sha1 value should never change
1973 tag_name = self.revisionExpr[len(R_TAGS):]
1974
1975 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001976 if self._CheckForImmutableRevision():
1977 print('Skipped fetching project %s (already have persistent ref)'
1978 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001979 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001980 if is_sha1 and not depth:
1981 # When syncing a specific commit and --depth is not set:
1982 # * if upstream is explicitly specified and is not a sha1, fetch only
1983 # upstream as users expect only upstream to be fetch.
1984 # Note: The commit might not be in upstream in which case the sync
1985 # will fail.
1986 # * otherwise, fetch all branches to make sure we end up with the
1987 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001988 if self.upstream:
1989 current_branch_only = not ID_RE.match(self.upstream)
1990 else:
1991 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001992
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001993 if not name:
1994 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001995
1996 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001997 remote = self.GetRemote(name)
1998 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001999 ssh_proxy = True
2000
Shawn O. Pearce88443382010-10-08 10:02:09 +02002001 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002002 if alt_dir and 'objects' == os.path.basename(alt_dir):
2003 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002004 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2005 remote = self.GetRemote(name)
2006
David Pursehouse8a68ff92012-09-24 12:15:13 +09002007 all_refs = self.bare_ref.all
2008 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002009 tmp = set()
2010
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302011 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002012 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002013 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002014 all_refs[r] = ref_id
2015 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002016 continue
2017
David Pursehouse8a68ff92012-09-24 12:15:13 +09002018 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 continue
2020
David Pursehouse8a68ff92012-09-24 12:15:13 +09002021 r = 'refs/_alt/%s' % ref_id
2022 all_refs[r] = ref_id
2023 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002024 tmp.add(r)
2025
heping3d7bbc92017-04-12 19:51:47 +08002026 tmp_packed_lines = []
2027 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002028
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302029 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002030 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002031 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002032 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002033 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034
heping3d7bbc92017-04-12 19:51:47 +08002035 tmp_packed = ''.join(tmp_packed_lines)
2036 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002037 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002039 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002040
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002041 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002042
Conley Owensf97e8382015-01-21 11:12:46 -08002043 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002044 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002045 else:
2046 # If this repo has shallow objects, then we don't know which refs have
2047 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2048 # do this with projects that don't have shallow objects, since it is less
2049 # efficient.
2050 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2051 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002052
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002053 if quiet:
2054 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002055 if not self.worktree:
2056 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002057 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002058
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002059 # If using depth then we should not get all the tags since they may
2060 # be outside of the depth.
2061 if no_tags or depth:
2062 cmd.append('--no-tags')
2063 else:
2064 cmd.append('--tags')
2065
David Pursehouse74cfd272015-10-14 10:50:15 +09002066 if prune:
2067 cmd.append('--prune')
2068
Martin Kellye4e94d22017-03-21 16:05:12 -07002069 if submodules:
2070 cmd.append('--recurse-submodules=on-demand')
2071
Conley Owens80b87fe2014-05-09 17:13:44 -07002072 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002073 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002074 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002075 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002076 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002077 spec.append('tag')
2078 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002079
David Pursehouse403b64e2015-04-27 10:41:33 +09002080 if not self.manifest.IsMirror:
2081 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002082 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002083 # Shallow checkout of a specific commit, fetch from that commit and not
2084 # the heads only as the commit might be deeper in the history.
2085 spec.append(branch)
2086 else:
2087 if is_sha1:
2088 branch = self.upstream
2089 if branch is not None and branch.strip():
2090 if not branch.startswith('refs/'):
2091 branch = R_HEADS + branch
2092 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002093 cmd.extend(spec)
2094
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002095 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002096 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002097 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002098 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002099 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002100 ok = True
2101 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002102 # If needed, run the 'git remote prune' the first time through the loop
2103 elif (not _i and
2104 "error:" in gitcmd.stderr and
2105 "git remote prune" in gitcmd.stderr):
2106 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002107 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002108 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002109 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002110 break
2111 continue
Brian Harring14a66742012-09-28 20:21:57 -07002112 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002113 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2114 # in sha1 mode, we just tried sync'ing from the upstream field; it
2115 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002116 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002117 elif ret < 0:
2118 # Git died with a signal, exit immediately
2119 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002120 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002121
2122 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002123 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002124 if old_packed != '':
2125 _lwrite(packed_refs, old_packed)
2126 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002127 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002128 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002129
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002130 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002131 # We just synced the upstream given branch; verify we
2132 # got what we wanted, else trigger a second run of all
2133 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002134 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002135 if current_branch_only and depth:
2136 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002137 return self._RemoteFetch(name=name,
2138 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002139 initial=False, quiet=quiet, alt_dir=alt_dir,
2140 depth=None)
2141 else:
2142 # Avoid infinite recursion: sync all branches with depth set to None
2143 return self._RemoteFetch(name=name, current_branch_only=False,
2144 initial=False, quiet=quiet, alt_dir=alt_dir,
2145 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002146
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002147 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002148
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002149 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002150 if initial and \
2151 (self.manifest.manifestProject.config.GetString('repo.depth') or
2152 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002153 return False
2154
2155 remote = self.GetRemote(self.remote.name)
2156 bundle_url = remote.url + '/clone.bundle'
2157 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002158 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2159 'persistent-http',
2160 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002161 return False
2162
2163 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2164 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2165
2166 exist_dst = os.path.exists(bundle_dst)
2167 exist_tmp = os.path.exists(bundle_tmp)
2168
2169 if not initial and not exist_dst and not exist_tmp:
2170 return False
2171
2172 if not exist_dst:
2173 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2174 if not exist_dst:
2175 return False
2176
2177 cmd = ['fetch']
2178 if quiet:
2179 cmd.append('--quiet')
2180 if not self.worktree:
2181 cmd.append('--update-head-ok')
2182 cmd.append(bundle_dst)
2183 for f in remote.fetch:
2184 cmd.append(str(f))
2185 cmd.append('refs/tags/*:refs/tags/*')
2186
2187 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002188 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002189 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002190 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002191 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002192 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002193
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002194 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002195 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002196 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002197
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002198 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002199 if quiet:
2200 cmd += ['--silent']
2201 if os.path.exists(tmpPath):
2202 size = os.stat(tmpPath).st_size
2203 if size >= 1024:
2204 cmd += ['--continue-at', '%d' % (size,)]
2205 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002206 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002207 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2208 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002209 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002210 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002211 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002212 if srcUrl.startswith('persistent-'):
2213 srcUrl = srcUrl[len('persistent-'):]
2214 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002215
Dave Borowitz137d0132015-01-02 11:12:54 -08002216 if IsTrace():
2217 Trace('%s', ' '.join(cmd))
2218 try:
2219 proc = subprocess.Popen(cmd)
2220 except OSError:
2221 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002222
Dave Borowitz137d0132015-01-02 11:12:54 -08002223 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002224
Dave Borowitz137d0132015-01-02 11:12:54 -08002225 if curlret == 22:
2226 # From curl man page:
2227 # 22: HTTP page not retrieved. The requested url was not found or
2228 # returned another error with the HTTP error code being 400 or above.
2229 # This return code only appears if -f, --fail is used.
2230 if not quiet:
2231 print("Server does not provide clone.bundle; ignoring.",
2232 file=sys.stderr)
2233 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002234
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002235 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002236 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002237 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002238 return True
2239 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002240 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002241 return False
2242 else:
2243 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002244
Kris Giesingc8d882a2014-12-23 13:02:32 -08002245 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002246 try:
2247 with open(path) as f:
2248 if f.read(16) == '# v2 git bundle\n':
2249 return True
2250 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002251 if not quiet:
2252 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002253 return False
2254 except OSError:
2255 return False
2256
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002257 def _Checkout(self, rev, quiet=False):
2258 cmd = ['checkout']
2259 if quiet:
2260 cmd.append('-q')
2261 cmd.append(rev)
2262 cmd.append('--')
2263 if GitCommand(self, cmd).Wait() != 0:
2264 if self._allrefs:
2265 raise GitError('%s checkout %s ' % (self.name, rev))
2266
Anthony King7bdac712014-07-16 12:56:40 +01002267 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002268 cmd = ['cherry-pick']
2269 cmd.append(rev)
2270 cmd.append('--')
2271 if GitCommand(self, cmd).Wait() != 0:
2272 if self._allrefs:
2273 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2274
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302275 def _LsRemote(self, refs):
2276 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302277 p = GitCommand(self, cmd, capture_stdout=True)
2278 if p.Wait() == 0:
2279 if hasattr(p.stdout, 'decode'):
2280 return p.stdout.decode('utf-8')
2281 else:
2282 return p.stdout
2283 return None
2284
Anthony King7bdac712014-07-16 12:56:40 +01002285 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002286 cmd = ['revert']
2287 cmd.append('--no-edit')
2288 cmd.append(rev)
2289 cmd.append('--')
2290 if GitCommand(self, cmd).Wait() != 0:
2291 if self._allrefs:
2292 raise GitError('%s revert %s ' % (self.name, rev))
2293
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002294 def _ResetHard(self, rev, quiet=True):
2295 cmd = ['reset', '--hard']
2296 if quiet:
2297 cmd.append('-q')
2298 cmd.append(rev)
2299 if GitCommand(self, cmd).Wait() != 0:
2300 raise GitError('%s reset --hard %s ' % (self.name, rev))
2301
Martin Kellye4e94d22017-03-21 16:05:12 -07002302 def _SyncSubmodules(self, quiet=True):
2303 cmd = ['submodule', 'update', '--init', '--recursive']
2304 if quiet:
2305 cmd.append('-q')
2306 if GitCommand(self, cmd).Wait() != 0:
2307 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2308
Anthony King7bdac712014-07-16 12:56:40 +01002309 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002310 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002311 if onto is not None:
2312 cmd.extend(['--onto', onto])
2313 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002314 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002315 raise GitError('%s rebase %s ' % (self.name, upstream))
2316
Pierre Tardy3d125942012-05-04 12:18:12 +02002317 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002318 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002319 if ffonly:
2320 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002321 if GitCommand(self, cmd).Wait() != 0:
2322 raise GitError('%s merge %s ' % (self.name, head))
2323
Kevin Degiabaa7f32014-11-12 11:27:45 -07002324 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002325 init_git_dir = not os.path.exists(self.gitdir)
2326 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002327 try:
2328 # Initialize the bare repository, which contains all of the objects.
2329 if init_obj_dir:
2330 os.makedirs(self.objdir)
2331 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002332
Kevin Degib1a07b82015-07-27 13:33:43 -06002333 # If we have a separate directory to hold refs, initialize it as well.
2334 if self.objdir != self.gitdir:
2335 if init_git_dir:
2336 os.makedirs(self.gitdir)
2337
2338 if init_obj_dir or init_git_dir:
2339 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2340 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002341 try:
2342 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2343 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002344 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002345 print("Retrying clone after deleting %s" %
2346 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002347 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002348 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2349 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002350 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002351 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002352 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2353 except:
2354 raise e
2355 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002356
Kevin Degi384b3c52014-10-16 16:02:58 -06002357 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002358 mp = self.manifest.manifestProject
2359 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002360
Kevin Degib1a07b82015-07-27 13:33:43 -06002361 if ref_dir or mirror_git:
2362 if not mirror_git:
2363 mirror_git = os.path.join(ref_dir, self.name + '.git')
2364 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2365 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002366
Kevin Degib1a07b82015-07-27 13:33:43 -06002367 if os.path.exists(mirror_git):
2368 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002369
Kevin Degib1a07b82015-07-27 13:33:43 -06002370 elif os.path.exists(repo_git):
2371 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002372
Kevin Degib1a07b82015-07-27 13:33:43 -06002373 else:
2374 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002375
Kevin Degib1a07b82015-07-27 13:33:43 -06002376 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002377 if not os.path.isabs(ref_dir):
2378 # The alternate directory is relative to the object database.
2379 ref_dir = os.path.relpath(ref_dir,
2380 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002381 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2382 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002383
Kevin Degib1a07b82015-07-27 13:33:43 -06002384 self._UpdateHooks()
2385
2386 m = self.manifest.manifestProject.config
2387 for key in ['user.name', 'user.email']:
2388 if m.Has(key, include_defaults=False):
2389 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002390 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002391 if self.manifest.IsMirror:
2392 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002393 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002394 self.config.SetString('core.bare', None)
2395 except Exception:
2396 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002397 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002398 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002399 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002400 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002401
Jimmie Westera0444582012-10-24 13:44:42 +02002402 def _UpdateHooks(self):
2403 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002404 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002405
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002406 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002407 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002408 if not os.path.exists(hooks):
2409 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002410 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002411 name = os.path.basename(stock_hook)
2412
Victor Boivie65e0f352011-04-18 11:23:29 +02002413 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002414 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002415 # Don't install a Gerrit Code Review hook if this
2416 # project does not appear to use it for reviews.
2417 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002418 # Since the manifest project is one of those, but also
2419 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002420 continue
2421
2422 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002423 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002424 continue
2425 if os.path.exists(dst):
2426 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002427 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002428 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002429 _warn("%s: Not replacing locally modified %s hook",
2430 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002431 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002432 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002433 platform_utils.symlink(
2434 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002435 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002436 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002437 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002438 else:
2439 raise
2440
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002441 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002442 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002443 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002444 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002445 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002446 remote.review = self.remote.review
2447 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002448
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002449 if self.worktree:
2450 remote.ResetFetch(mirror=False)
2451 else:
2452 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002453 remote.Save()
2454
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002455 def _InitMRef(self):
2456 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002457 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002458
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002459 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002460 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002461
2462 def _InitAnyMRef(self, ref):
2463 cur = self.bare_ref.symref(ref)
2464
2465 if self.revisionId:
2466 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2467 msg = 'manifest set to %s' % self.revisionId
2468 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002469 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002470 else:
2471 remote = self.GetRemote(self.remote.name)
2472 dst = remote.ToLocal(self.revisionExpr)
2473 if cur != dst:
2474 msg = 'manifest set to %s' % self.revisionExpr
2475 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002476
Kevin Degi384b3c52014-10-16 16:02:58 -06002477 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002478 symlink_files = self.shareable_files[:]
2479 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002480 if share_refs:
2481 symlink_files += self.working_tree_files
2482 symlink_dirs += self.working_tree_dirs
2483 to_symlink = symlink_files + symlink_dirs
2484 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002485 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002486 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002487 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002488 # Fail if the links are pointing to the wrong place
2489 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002490 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002491 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002492 'work tree. If you\'re comfortable with the '
2493 'possibility of losing the work tree\'s git metadata,'
2494 ' use `repo sync --force-sync {0}` to '
2495 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002496
David James8d201162013-10-11 17:03:19 -07002497 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2498 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2499
2500 Args:
2501 gitdir: The bare git repository. Must already be initialized.
2502 dotgit: The repository you would like to initialize.
2503 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2504 Only one work tree can store refs under a given |gitdir|.
2505 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2506 This saves you the effort of initializing |dotgit| yourself.
2507 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002508 symlink_files = self.shareable_files[:]
2509 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002510 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002511 symlink_files += self.working_tree_files
2512 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002513 to_symlink = symlink_files + symlink_dirs
2514
2515 to_copy = []
2516 if copy_all:
Renaud Paquaybed8b622018-09-27 10:46:58 -07002517 to_copy = platform_utils.listdir(gitdir)
David James8d201162013-10-11 17:03:19 -07002518
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002519 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002520 for name in set(to_copy).union(to_symlink):
2521 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002522 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002523 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002524
Kevin Degi384b3c52014-10-16 16:02:58 -06002525 if os.path.lexists(dst):
2526 continue
David James8d201162013-10-11 17:03:19 -07002527
2528 # If the source dir doesn't exist, create an empty dir.
2529 if name in symlink_dirs and not os.path.lexists(src):
2530 os.makedirs(src)
2531
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002532 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002533 platform_utils.symlink(
2534 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002535 elif copy_all and not platform_utils.islink(dst):
Renaud Paquaybed8b622018-09-27 10:46:58 -07002536 if platform_utils.isdir(src):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002537 shutil.copytree(src, dst)
2538 elif os.path.isfile(src):
2539 shutil.copy(src, dst)
2540
Conley Owens80b87fe2014-05-09 17:13:44 -07002541 # If the source file doesn't exist, ensure the destination
2542 # file doesn't either.
2543 if name in symlink_files and not os.path.lexists(src):
2544 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002545 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002546 except OSError:
2547 pass
2548
David James8d201162013-10-11 17:03:19 -07002549 except OSError as e:
2550 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002551 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002552 else:
2553 raise
2554
Martin Kellye4e94d22017-03-21 16:05:12 -07002555 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002556 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002557 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002558 try:
2559 if init_dotgit:
2560 os.makedirs(dotgit)
2561 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2562 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002563
Kevin Degiabaa7f32014-11-12 11:27:45 -07002564 try:
2565 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2566 except GitError as e:
2567 if force_sync:
2568 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002569 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002570 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002571 except:
2572 raise e
2573 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002574
Kevin Degib1a07b82015-07-27 13:33:43 -06002575 if init_dotgit:
2576 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002577
Kevin Degib1a07b82015-07-27 13:33:43 -06002578 cmd = ['read-tree', '--reset', '-u']
2579 cmd.append('-v')
2580 cmd.append(HEAD)
2581 if GitCommand(self, cmd).Wait() != 0:
2582 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002583
Martin Kellye4e94d22017-03-21 16:05:12 -07002584 if submodules:
2585 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002586 self._CopyAndLinkFiles()
2587 except Exception:
2588 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002589 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002590 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002591
Renaud Paquay788e9622017-01-27 11:41:12 -08002592 def _get_symlink_error_message(self):
2593 if platform_utils.isWindows():
2594 return ('Unable to create symbolic link. Please re-run the command as '
2595 'Administrator, or see '
2596 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2597 'for other options.')
2598 return 'filesystem must support symlinks'
2599
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002600 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002601 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002602
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002603 def _revlist(self, *args, **kw):
2604 a = []
2605 a.extend(args)
2606 a.append('--')
2607 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002608
2609 @property
2610 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002611 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002612
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002613 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002614 """Get logs between two revisions of this project."""
2615 comp = '..'
2616 if rev1:
2617 revs = [rev1]
2618 if rev2:
2619 revs.extend([comp, rev2])
2620 cmd = ['log', ''.join(revs)]
2621 out = DiffColoring(self.config)
2622 if out.is_on and color:
2623 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002624 if pretty_format is not None:
2625 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002626 if oneline:
2627 cmd.append('--oneline')
2628
2629 try:
2630 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2631 if log.Wait() == 0:
2632 return log.stdout
2633 except GitError:
2634 # worktree may not exist if groups changed for example. In that case,
2635 # try in gitdir instead.
2636 if not os.path.exists(self.worktree):
2637 return self.bare_git.log(*cmd[1:])
2638 else:
2639 raise
2640 return None
2641
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002642 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2643 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002644 """Get the list of logs from this revision to given revisionId"""
2645 logs = {}
2646 selfId = self.GetRevisionId(self._allrefs)
2647 toId = toProject.GetRevisionId(toProject._allrefs)
2648
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002649 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2650 pretty_format=pretty_format)
2651 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2652 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002653 return logs
2654
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002655 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002656
David James8d201162013-10-11 17:03:19 -07002657 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002658 self._project = project
2659 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002660 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002661
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002662 def LsOthers(self):
2663 p = GitCommand(self._project,
2664 ['ls-files',
2665 '-z',
2666 '--others',
2667 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002668 bare=False,
David James8d201162013-10-11 17:03:19 -07002669 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002670 capture_stdout=True,
2671 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002672 if p.Wait() == 0:
2673 out = p.stdout
2674 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002675 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002676 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002677 return []
2678
2679 def DiffZ(self, name, *args):
2680 cmd = [name]
2681 cmd.append('-z')
2682 cmd.extend(args)
2683 p = GitCommand(self._project,
2684 cmd,
David James8d201162013-10-11 17:03:19 -07002685 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002686 bare=False,
2687 capture_stdout=True,
2688 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002689 try:
2690 out = p.process.stdout.read()
2691 r = {}
2692 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002693 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002694 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002695 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002696 info = next(out)
2697 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002698 except StopIteration:
2699 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002700
2701 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002702
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002703 def __init__(self, path, omode, nmode, oid, nid, state):
2704 self.path = path
2705 self.src_path = None
2706 self.old_mode = omode
2707 self.new_mode = nmode
2708 self.old_id = oid
2709 self.new_id = nid
2710
2711 if len(state) == 1:
2712 self.status = state
2713 self.level = None
2714 else:
2715 self.status = state[:1]
2716 self.level = state[1:]
2717 while self.level.startswith('0'):
2718 self.level = self.level[1:]
2719
2720 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002721 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002722 if info.status in ('R', 'C'):
2723 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002724 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002725 r[info.path] = info
2726 return r
2727 finally:
2728 p.Wait()
2729
2730 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002731 if self._bare:
2732 path = os.path.join(self._project.gitdir, HEAD)
2733 else:
2734 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002735 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002736 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002737 except IOError as e:
2738 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002739 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002740 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002741 finally:
2742 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302743 try:
2744 line = line.decode()
2745 except AttributeError:
2746 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002747 if line.startswith('ref: '):
2748 return line[5:-1]
2749 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002750
2751 def SetHead(self, ref, message=None):
2752 cmdv = []
2753 if message is not None:
2754 cmdv.extend(['-m', message])
2755 cmdv.append(HEAD)
2756 cmdv.append(ref)
2757 self.symbolic_ref(*cmdv)
2758
2759 def DetachHead(self, new, message=None):
2760 cmdv = ['--no-deref']
2761 if message is not None:
2762 cmdv.extend(['-m', message])
2763 cmdv.append(HEAD)
2764 cmdv.append(new)
2765 self.update_ref(*cmdv)
2766
2767 def UpdateRef(self, name, new, old=None,
2768 message=None,
2769 detach=False):
2770 cmdv = []
2771 if message is not None:
2772 cmdv.extend(['-m', message])
2773 if detach:
2774 cmdv.append('--no-deref')
2775 cmdv.append(name)
2776 cmdv.append(new)
2777 if old is not None:
2778 cmdv.append(old)
2779 self.update_ref(*cmdv)
2780
2781 def DeleteRef(self, name, old=None):
2782 if not old:
2783 old = self.rev_parse(name)
2784 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002785 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002786
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002787 def rev_list(self, *args, **kw):
2788 if 'format' in kw:
2789 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2790 else:
2791 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002792 cmdv.extend(args)
2793 p = GitCommand(self._project,
2794 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002795 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002796 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002797 capture_stdout=True,
2798 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002799 r = []
2800 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002801 if line[-1] == '\n':
2802 line = line[:-1]
2803 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002804 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002805 raise GitError('%s rev-list %s: %s' %
2806 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002807 return r
2808
2809 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002810 """Allow arbitrary git commands using pythonic syntax.
2811
2812 This allows you to do things like:
2813 git_obj.rev_parse('HEAD')
2814
2815 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2816 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002817 Any other positional arguments will be passed to the git command, and the
2818 following keyword arguments are supported:
2819 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002820
2821 Args:
2822 name: The name of the git command to call. Any '_' characters will
2823 be replaced with '-'.
2824
2825 Returns:
2826 A callable object that will try to call git with the named command.
2827 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002828 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002829
Dave Borowitz091f8932012-10-23 17:01:04 -07002830 def runner(*args, **kwargs):
2831 cmdv = []
2832 config = kwargs.pop('config', None)
2833 for k in kwargs:
2834 raise TypeError('%s() got an unexpected keyword argument %r'
2835 % (name, k))
2836 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002837 if not git_require((1, 7, 2)):
2838 raise ValueError('cannot set config on command line for %s()'
2839 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302840 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002841 cmdv.append('-c')
2842 cmdv.append('%s=%s' % (k, v))
2843 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002844 cmdv.extend(args)
2845 p = GitCommand(self._project,
2846 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002847 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002848 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002849 capture_stdout=True,
2850 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002851 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002852 raise GitError('%s %s: %s' %
2853 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002854 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302855 try:
Conley Owensedd01512013-09-26 12:59:58 -07002856 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302857 except AttributeError:
2858 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002859 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2860 return r[:-1]
2861 return r
2862 return runner
2863
2864
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002865class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002866
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002867 def __str__(self):
2868 return 'prior sync failed; rebase still in progress'
2869
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002870
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002872
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002873 def __str__(self):
2874 return 'contains uncommitted changes'
2875
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002876
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002877class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002878
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002879 def __init__(self, project, text):
2880 self.project = project
2881 self.text = text
2882
2883 def Print(self, syncbuf):
2884 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2885 syncbuf.out.nl()
2886
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002887
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002888class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002889
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002890 def __init__(self, project, why):
2891 self.project = project
2892 self.why = why
2893
2894 def Print(self, syncbuf):
2895 syncbuf.out.fail('error: %s/: %s',
2896 self.project.relpath,
2897 str(self.why))
2898 syncbuf.out.nl()
2899
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002900
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002901class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002902
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002903 def __init__(self, project, action):
2904 self.project = project
2905 self.action = action
2906
2907 def Run(self, syncbuf):
2908 out = syncbuf.out
2909 out.project('project %s/', self.project.relpath)
2910 out.nl()
2911 try:
2912 self.action()
2913 out.nl()
2914 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002915 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002916 out.nl()
2917 return False
2918
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002919
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002920class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002921
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002922 def __init__(self, config):
2923 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002924 self.project = self.printer('header', attr='bold')
2925 self.info = self.printer('info')
2926 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002927
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002928
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002929class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002930
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002931 def __init__(self, config, detach_head=False):
2932 self._messages = []
2933 self._failures = []
2934 self._later_queue1 = []
2935 self._later_queue2 = []
2936
2937 self.out = _SyncColoring(config)
2938 self.out.redirect(sys.stderr)
2939
2940 self.detach_head = detach_head
2941 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002942 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002943
2944 def info(self, project, fmt, *args):
2945 self._messages.append(_InfoMessage(project, fmt % args))
2946
2947 def fail(self, project, err=None):
2948 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002949 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002950
2951 def later1(self, project, what):
2952 self._later_queue1.append(_Later(project, what))
2953
2954 def later2(self, project, what):
2955 self._later_queue2.append(_Later(project, what))
2956
2957 def Finish(self):
2958 self._PrintMessages()
2959 self._RunLater()
2960 self._PrintMessages()
2961 return self.clean
2962
David Rileye0684ad2017-04-05 00:02:59 -07002963 def Recently(self):
2964 recent_clean = self.recent_clean
2965 self.recent_clean = True
2966 return recent_clean
2967
2968 def _MarkUnclean(self):
2969 self.clean = False
2970 self.recent_clean = False
2971
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002972 def _RunLater(self):
2973 for q in ['_later_queue1', '_later_queue2']:
2974 if not self._RunQueue(q):
2975 return
2976
2977 def _RunQueue(self, queue):
2978 for m in getattr(self, queue):
2979 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002980 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002981 return False
2982 setattr(self, queue, [])
2983 return True
2984
2985 def _PrintMessages(self):
2986 for m in self._messages:
2987 m.Print(self)
2988 for m in self._failures:
2989 m.Print(self)
2990
2991 self._messages = []
2992 self._failures = []
2993
2994
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002995class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002996
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002997 """A special project housed under .repo.
2998 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002999
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003000 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003001 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003002 manifest=manifest,
3003 name=name,
3004 gitdir=gitdir,
3005 objdir=gitdir,
3006 worktree=worktree,
3007 remote=RemoteSpec('origin'),
3008 relpath='.repo/%s' % name,
3009 revisionExpr='refs/heads/master',
3010 revisionId=None,
3011 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003012
3013 def PreSync(self):
3014 if self.Exists:
3015 cb = self.CurrentBranch
3016 if cb:
3017 base = self.GetBranch(cb).merge
3018 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003019 self.revisionExpr = base
3020 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003021
Martin Kelly224a31a2017-07-10 14:46:25 -07003022 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003023 """ Prepare MetaProject for manifest branch switch
3024 """
3025
3026 # detach and delete manifest branch, allowing a new
3027 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003028 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003029 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003030 syncbuf.Finish()
3031
3032 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003033 ['update-ref', '-d', 'refs/heads/default'],
3034 capture_stdout=True,
3035 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003036
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003037 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003038 def LastFetch(self):
3039 try:
3040 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3041 return os.path.getmtime(fh)
3042 except OSError:
3043 return 0
3044
3045 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003046 def HasChanges(self):
3047 """Has the remote received new commits not yet checked out?
3048 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003049 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003050 return False
3051
David Pursehouse8a68ff92012-09-24 12:15:13 +09003052 all_refs = self.bare_ref.all
3053 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003054 head = self.work_git.GetHead()
3055 if head.startswith(R_HEADS):
3056 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003057 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003058 except KeyError:
3059 head = None
3060
3061 if revid == head:
3062 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003063 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003064 return True
3065 return False