blob: a7001b0168020a0b5d248f6a3d3207a068ccae36 [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')
106 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
107 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)
256 if not os.path.isdir(dest_dir):
257 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)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 if not os.path.isdir(dest_dir):
286 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
305 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
306 _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):
Kevin Degi384b3c52014-10-16 16:02:58 -0600753 return os.path.isdir(self.gitdir) and os.path.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 """
934 if not os.path.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://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001155 rp = ['gerrit receive-pack']
1156 for e in people[0]:
1157 rp.append('--reviewer=%s' % sq(e))
1158 for e in people[1]:
1159 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001160 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001161
Masaya Suzuki305a2d02017-11-13 10:48:34 -08001162 for push_option in (push_options or []):
1163 cmd.append('-o')
1164 cmd.append(push_option)
1165
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001167
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001168 if dest_branch.startswith(R_HEADS):
1169 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001170
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001171 upload_type = 'for'
1172 if draft:
1173 upload_type = 'drafts'
1174
1175 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1176 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001177 if auto_topic:
1178 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001179
Shawn Pearce45d21682013-02-28 00:35:51 -08001180 if not url.startswith('ssh://'):
1181 rp = ['r=%s' % p for p in people[0]] + \
1182 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001183 if private:
1184 rp = rp + ['private']
1185 if wip:
1186 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001187 if rp:
1188 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001189 cmd.append(ref_spec)
1190
Anthony King7bdac712014-07-16 12:56:40 +01001191 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001192 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001193
1194 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1195 self.bare_git.UpdateRef(R_PUB + branch.name,
1196 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001197 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001198
1199
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001200# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001201
Julien Campergue335f5ef2013-10-16 11:02:35 +02001202 def _ExtractArchive(self, tarpath, path=None):
1203 """Extract the given tar on its current location
1204
1205 Args:
1206 - tarpath: The path to the actual tar file
1207
1208 """
1209 try:
1210 with tarfile.open(tarpath, 'r') as tar:
1211 tar.extractall(path=path)
1212 return True
1213 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001214 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001215 return False
1216
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001217 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001218 quiet=False,
1219 is_new=None,
1220 current_branch_only=False,
1221 force_sync=False,
1222 clone_bundle=True,
1223 no_tags=False,
1224 archive=False,
1225 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001226 prune=False,
1227 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001228 """Perform only the network IO portion of the sync process.
1229 Local working directory/branch state is not affected.
1230 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001231 if archive and not isinstance(self, MetaProject):
1232 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001233 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001234 return False
1235
1236 name = self.relpath.replace('\\', '/')
1237 name = name.replace('/', '_')
1238 tarpath = '%s.tar' % name
1239 topdir = self.manifest.topdir
1240
1241 try:
1242 self._FetchArchive(tarpath, cwd=topdir)
1243 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001244 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001245 return False
1246
1247 # From now on, we only need absolute tarpath
1248 tarpath = os.path.join(topdir, tarpath)
1249
1250 if not self._ExtractArchive(tarpath, path=topdir):
1251 return False
1252 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001253 platform_utils.remove(tarpath)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001254 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001255 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001256 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001257 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001258 if is_new is None:
1259 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001260 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001261 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001262 else:
1263 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001264 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001265
1266 if is_new:
1267 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1268 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07001269 fd = open(alt)
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001270 try:
Samuel Hollandbaa00092018-01-22 10:57:29 -06001271 # This works for both absolute and relative alternate directories.
1272 alt_dir = os.path.join(self.objdir, 'objects', fd.readline().rstrip())
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001273 finally:
1274 fd.close()
1275 except IOError:
1276 alt_dir = None
1277 else:
1278 alt_dir = None
1279
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001280 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001281 and alt_dir is None \
1282 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001283 is_new = False
1284
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001285 if not current_branch_only:
1286 if self.sync_c:
1287 current_branch_only = True
1288 elif not self.manifest._loaded:
1289 # Manifest cannot check defaults until it syncs.
1290 current_branch_only = False
1291 elif self.manifest.default.sync_c:
1292 current_branch_only = True
1293
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001294 if not no_tags:
1295 if not self.sync_tags:
1296 no_tags = True
1297
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001298 if self.clone_depth:
1299 depth = self.clone_depth
1300 else:
1301 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1302
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001303 need_to_fetch = not (optimized_fetch and
1304 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001305 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001306 if (need_to_fetch and
1307 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1308 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001309 no_tags=no_tags, prune=prune, depth=depth,
1310 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001311 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001312
Nikolai Merinov09f0abb2018-10-19 15:07:05 +05001313 mp = self.manifest.manifestProject
1314 dissociate = mp.config.GetBoolean('repo.dissociate')
1315 if dissociate:
1316 alternates_file = os.path.join(self.gitdir, 'objects/info/alternates')
1317 if os.path.exists(alternates_file):
1318 cmd = ['repack', '-a', '-d']
1319 if GitCommand(self, cmd, bare=True).Wait() != 0:
1320 return False
1321 platform_utils.remove(alternates_file)
1322
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001323 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001324 self._InitMRef()
1325 else:
1326 self._InitMirrorHead()
1327 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08001328 platform_utils.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001329 except OSError:
1330 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001331 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001332
1333 def PostRepoUpgrade(self):
1334 self._InitHooks()
1335
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001336 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001337 if self.manifest.isGitcClient:
1338 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001339 for copyfile in self.copyfiles:
1340 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001341 for linkfile in self.linkfiles:
1342 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001343
Julien Camperguedd654222014-01-09 16:21:37 +01001344 def GetCommitRevisionId(self):
1345 """Get revisionId of a commit.
1346
1347 Use this method instead of GetRevisionId to get the id of the commit rather
1348 than the id of the current git object (for example, a tag)
1349
1350 """
1351 if not self.revisionExpr.startswith(R_TAGS):
1352 return self.GetRevisionId(self._allrefs)
1353
1354 try:
1355 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1356 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001357 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1358 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001359
David Pursehouse8a68ff92012-09-24 12:15:13 +09001360 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001361 if self.revisionId:
1362 return self.revisionId
1363
1364 rem = self.GetRemote(self.remote.name)
1365 rev = rem.ToLocal(self.revisionExpr)
1366
David Pursehouse8a68ff92012-09-24 12:15:13 +09001367 if all_refs is not None and rev in all_refs:
1368 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001369
1370 try:
1371 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1372 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001373 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1374 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001375
Martin Kellye4e94d22017-03-21 16:05:12 -07001376 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001377 """Perform only the local IO portion of the sync process.
1378 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001379 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001380 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001381 all_refs = self.bare_ref.all
1382 self.CleanPublishedCache(all_refs)
1383 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001384
David Pursehouse1d947b32012-10-25 12:23:11 +09001385 def _doff():
1386 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001387 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001388
Martin Kellye4e94d22017-03-21 16:05:12 -07001389 def _dosubmodules():
1390 self._SyncSubmodules(quiet=True)
1391
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001392 head = self.work_git.GetHead()
1393 if head.startswith(R_HEADS):
1394 branch = head[len(R_HEADS):]
1395 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001396 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001397 except KeyError:
1398 head = None
1399 else:
1400 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001401
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001402 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001403 # Currently on a detached HEAD. The user is assumed to
1404 # not have any local modifications worth worrying about.
1405 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001406 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001407 syncbuf.fail(self, _PriorSyncFailedError())
1408 return
1409
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001410 if head == revid:
1411 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001412 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001413 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001414 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001415 # The copy/linkfile config may have changed.
1416 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001417 return
1418 else:
1419 lost = self._revlist(not_rev(revid), HEAD)
1420 if lost:
1421 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001422
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001424 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001425 if submodules:
1426 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001427 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001428 syncbuf.fail(self, e)
1429 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001430 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001431 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001432
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001433 if head == revid:
1434 # No changes; don't do anything further.
1435 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001436 # The copy/linkfile config may have changed.
1437 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001438 return
1439
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001440 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001441
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001442 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001443 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001444 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001446 syncbuf.info(self,
1447 "leaving %s; does not track upstream",
1448 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001449 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001450 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001451 if submodules:
1452 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001453 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001454 syncbuf.fail(self, e)
1455 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001456 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001457 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001458
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001459 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001460 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001461 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001462 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001463 if not_merged:
1464 if upstream_gain:
1465 # The user has published this branch and some of those
1466 # commits are not yet merged upstream. We do not want
1467 # to rewrite the published commits so we punt.
1468 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001469 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001470 "branch %s is published (but not merged) and is now "
1471 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001472 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001473 elif pub == head:
1474 # All published commits are merged, and thus we are a
1475 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001476 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001477 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001478 if submodules:
1479 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001480 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001481
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001482 # Examine the local commits not in the remote. Find the
1483 # last one attributed to this user, if any.
1484 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001485 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001486 last_mine = None
1487 cnt_mine = 0
1488 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301489 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001490 if committer_email == self.UserEmail:
1491 last_mine = commit_id
1492 cnt_mine += 1
1493
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001494 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001495 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001496
1497 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001498 syncbuf.fail(self, _DirtyError())
1499 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001500
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001501 # If the upstream switched on us, warn the user.
1502 #
1503 if branch.merge != self.revisionExpr:
1504 if branch.merge and self.revisionExpr:
1505 syncbuf.info(self,
1506 'manifest switched %s...%s',
1507 branch.merge,
1508 self.revisionExpr)
1509 elif branch.merge:
1510 syncbuf.info(self,
1511 'manifest no longer tracks %s',
1512 branch.merge)
1513
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001514 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001516 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001517 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001518 syncbuf.info(self,
1519 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001520 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001521
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001522 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001523 if not ID_RE.match(self.revisionExpr):
1524 # in case of manifest sync the revisionExpr might be a SHA1
1525 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001526 if not branch.merge.startswith('refs/'):
1527 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001528 branch.Save()
1529
Mike Pontillod3153822012-02-28 11:53:24 -08001530 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001531 def _docopyandlink():
1532 self._CopyAndLinkFiles()
1533
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001534 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001535 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001536 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001537 if submodules:
1538 syncbuf.later2(self, _dosubmodules)
1539 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001540 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001541 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001542 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001543 if submodules:
1544 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001545 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001546 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001547 syncbuf.fail(self, e)
1548 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001549 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001550 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001551 if submodules:
1552 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001554 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 # dest should already be an absolute path, but src is project relative
1556 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001557 abssrc = os.path.join(self.worktree, src)
1558 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001560 def AddLinkFile(self, src, dest, absdest):
1561 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001562 # make src relative path to dest
1563 absdestdir = os.path.dirname(absdest)
1564 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001565 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001566
James W. Mills24c13082012-04-12 15:04:13 -05001567 def AddAnnotation(self, name, value, keep):
1568 self.annotations.append(_Annotation(name, value, keep))
1569
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001570 def DownloadPatchSet(self, change_id, patch_id):
1571 """Download a single patch set of a single change to FETCH_HEAD.
1572 """
1573 remote = self.GetRemote(self.remote.name)
1574
1575 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001576 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001577 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001578 if GitCommand(self, cmd, bare=True).Wait() != 0:
1579 return None
1580 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001581 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001582 change_id,
1583 patch_id,
1584 self.bare_git.rev_parse('FETCH_HEAD'))
1585
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001586
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001587# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001588
Simran Basib9a1b732015-08-20 12:19:28 -07001589 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001590 """Create a new branch off the manifest's revision.
1591 """
Simran Basib9a1b732015-08-20 12:19:28 -07001592 if not branch_merge:
1593 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001594 head = self.work_git.GetHead()
1595 if head == (R_HEADS + name):
1596 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001597
David Pursehouse8a68ff92012-09-24 12:15:13 +09001598 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001599 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001600 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001601 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001602 capture_stdout=True,
1603 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001604
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001605 branch = self.GetBranch(name)
1606 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001607 branch.merge = branch_merge
1608 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1609 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001610 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001611
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001612 if head.startswith(R_HEADS):
1613 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001614 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001615 except KeyError:
1616 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001617 if revid and head and revid == head:
1618 ref = os.path.join(self.gitdir, R_HEADS + name)
1619 try:
1620 os.makedirs(os.path.dirname(ref))
1621 except OSError:
1622 pass
1623 _lwrite(ref, '%s\n' % revid)
1624 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1625 'ref: %s%s\n' % (R_HEADS, name))
1626 branch.Save()
1627 return True
1628
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001629 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001630 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001631 capture_stdout=True,
1632 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001633 branch.Save()
1634 return True
1635 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001636
Wink Saville02d79452009-04-10 13:01:24 -07001637 def CheckoutBranch(self, name):
1638 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001639
1640 Args:
1641 name: The name of the branch to checkout.
1642
1643 Returns:
1644 True if the checkout succeeded; False if it didn't; None if the branch
1645 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001646 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001647 rev = R_HEADS + name
1648 head = self.work_git.GetHead()
1649 if head == rev:
1650 # Already on the branch
1651 #
1652 return True
Wink Saville02d79452009-04-10 13:01:24 -07001653
David Pursehouse8a68ff92012-09-24 12:15:13 +09001654 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001655 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001656 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001657 except KeyError:
1658 # Branch does not exist in this project
1659 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001660 return None
Wink Saville02d79452009-04-10 13:01:24 -07001661
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001662 if head.startswith(R_HEADS):
1663 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001664 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001665 except KeyError:
1666 head = None
1667
1668 if head == revid:
1669 # Same revision; just update HEAD to point to the new
1670 # target branch, but otherwise take no other action.
1671 #
1672 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1673 'ref: %s%s\n' % (R_HEADS, name))
1674 return True
1675
1676 return GitCommand(self,
1677 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001678 capture_stdout=True,
1679 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001680
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001681 def AbandonBranch(self, name):
1682 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001683
1684 Args:
1685 name: The name of the branch to abandon.
1686
1687 Returns:
1688 True if the abandon succeeded; False if it didn't; None if the branch
1689 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001690 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001691 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001692 all_refs = self.bare_ref.all
1693 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001694 # Doesn't exist
1695 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001696
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001697 head = self.work_git.GetHead()
1698 if head == rev:
1699 # We can't destroy the branch while we are sitting
1700 # on it. Switch to a detached HEAD.
1701 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001702 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001703
David Pursehouse8a68ff92012-09-24 12:15:13 +09001704 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001705 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001706 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1707 '%s\n' % revid)
1708 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001709 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001710
1711 return GitCommand(self,
1712 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001713 capture_stdout=True,
1714 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001715
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001716 def PruneHeads(self):
1717 """Prune any topic branches already merged into upstream.
1718 """
1719 cb = self.CurrentBranch
1720 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001721 left = self._allrefs
1722 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723 if name.startswith(R_HEADS):
1724 name = name[len(R_HEADS):]
1725 if cb is None or name != cb:
1726 kill.append(name)
1727
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001728 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001729 if cb is not None \
1730 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001731 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732 self.work_git.DetachHead(HEAD)
1733 kill.append(cb)
1734
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001735 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001736 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001737
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001738 try:
1739 self.bare_git.DetachHead(rev)
1740
1741 b = ['branch', '-d']
1742 b.extend(kill)
1743 b = GitCommand(self, b, bare=True,
1744 capture_stdout=True,
1745 capture_stderr=True)
1746 b.Wait()
1747 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001748 if ID_RE.match(old):
1749 self.bare_git.DetachHead(old)
1750 else:
1751 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001752 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001753
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001754 for branch in kill:
1755 if (R_HEADS + branch) not in left:
1756 self.CleanPublishedCache()
1757 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758
1759 if cb and cb not in kill:
1760 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001761 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001762
1763 kept = []
1764 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001765 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001766 branch = self.GetBranch(branch)
1767 base = branch.LocalMerge
1768 if not base:
1769 base = rev
1770 kept.append(ReviewableBranch(self, branch, base))
1771 return kept
1772
1773
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001774# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001775
1776 def GetRegisteredSubprojects(self):
1777 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001778
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001779 def rec(subprojects):
1780 if not subprojects:
1781 return
1782 result.extend(subprojects)
1783 for p in subprojects:
1784 rec(p.subprojects)
1785 rec(self.subprojects)
1786 return result
1787
1788 def _GetSubmodules(self):
1789 # Unfortunately we cannot call `git submodule status --recursive` here
1790 # because the working tree might not exist yet, and it cannot be used
1791 # without a working tree in its current implementation.
1792
1793 def get_submodules(gitdir, rev):
1794 # Parse .gitmodules for submodule sub_paths and sub_urls
1795 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1796 if not sub_paths:
1797 return []
1798 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1799 # revision of submodule repository
1800 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1801 submodules = []
1802 for sub_path, sub_url in zip(sub_paths, sub_urls):
1803 try:
1804 sub_rev = sub_revs[sub_path]
1805 except KeyError:
1806 # Ignore non-exist submodules
1807 continue
1808 submodules.append((sub_rev, sub_path, sub_url))
1809 return submodules
1810
1811 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1812 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001813
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001814 def parse_gitmodules(gitdir, rev):
1815 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1816 try:
Anthony King7bdac712014-07-16 12:56:40 +01001817 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1818 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001819 except GitError:
1820 return [], []
1821 if p.Wait() != 0:
1822 return [], []
1823
1824 gitmodules_lines = []
1825 fd, temp_gitmodules_path = tempfile.mkstemp()
1826 try:
1827 os.write(fd, p.stdout)
1828 os.close(fd)
1829 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001830 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1831 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001832 if p.Wait() != 0:
1833 return [], []
1834 gitmodules_lines = p.stdout.split('\n')
1835 except GitError:
1836 return [], []
1837 finally:
Renaud Paquay010fed72016-11-11 14:25:29 -08001838 platform_utils.remove(temp_gitmodules_path)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001839
1840 names = set()
1841 paths = {}
1842 urls = {}
1843 for line in gitmodules_lines:
1844 if not line:
1845 continue
1846 m = re_path.match(line)
1847 if m:
1848 names.add(m.group(1))
1849 paths[m.group(1)] = m.group(2)
1850 continue
1851 m = re_url.match(line)
1852 if m:
1853 names.add(m.group(1))
1854 urls[m.group(1)] = m.group(2)
1855 continue
1856 names = sorted(names)
1857 return ([paths.get(name, '') for name in names],
1858 [urls.get(name, '') for name in names])
1859
1860 def git_ls_tree(gitdir, rev, paths):
1861 cmd = ['ls-tree', rev, '--']
1862 cmd.extend(paths)
1863 try:
Anthony King7bdac712014-07-16 12:56:40 +01001864 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1865 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001866 except GitError:
1867 return []
1868 if p.Wait() != 0:
1869 return []
1870 objects = {}
1871 for line in p.stdout.split('\n'):
1872 if not line.strip():
1873 continue
1874 object_rev, object_path = line.split()[2:4]
1875 objects[object_path] = object_rev
1876 return objects
1877
1878 try:
1879 rev = self.GetRevisionId()
1880 except GitError:
1881 return []
1882 return get_submodules(self.gitdir, rev)
1883
1884 def GetDerivedSubprojects(self):
1885 result = []
1886 if not self.Exists:
1887 # If git repo does not exist yet, querying its submodules will
1888 # mess up its states; so return here.
1889 return result
1890 for rev, path, url in self._GetSubmodules():
1891 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001892 relpath, worktree, gitdir, objdir = \
1893 self.manifest.GetSubprojectPaths(self, name, path)
1894 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001895 if project:
1896 result.extend(project.GetDerivedSubprojects())
1897 continue
David James8d201162013-10-11 17:03:19 -07001898
Shouheng Zhang02c0ee62017-12-08 09:54:58 +08001899 if url.startswith('..'):
1900 url = urllib.parse.urljoin("%s/" % self.remote.url, url)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001901 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001902 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001903 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001904 review=self.remote.review,
1905 revision=self.remote.revision)
1906 subproject = Project(manifest=self.manifest,
1907 name=name,
1908 remote=remote,
1909 gitdir=gitdir,
1910 objdir=objdir,
1911 worktree=worktree,
1912 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001913 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001914 revisionId=rev,
1915 rebase=self.rebase,
1916 groups=self.groups,
1917 sync_c=self.sync_c,
1918 sync_s=self.sync_s,
YOUNG HO CHAa32c92c2018-02-14 16:57:31 +09001919 sync_tags=self.sync_tags,
Anthony King7bdac712014-07-16 12:56:40 +01001920 parent=self,
1921 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001922 result.append(subproject)
1923 result.extend(subproject.GetDerivedSubprojects())
1924 return result
1925
1926
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001927# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001928 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001929 try:
1930 # if revision (sha or tag) is not present then following function
1931 # throws an error.
1932 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1933 return True
1934 except GitError:
1935 # There is no such persistent revision. We have to fetch it.
1936 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001937
Julien Campergue335f5ef2013-10-16 11:02:35 +02001938 def _FetchArchive(self, tarpath, cwd=None):
1939 cmd = ['archive', '-v', '-o', tarpath]
1940 cmd.append('--remote=%s' % self.remote.url)
1941 cmd.append('--prefix=%s/' % self.relpath)
1942 cmd.append(self.revisionExpr)
1943
1944 command = GitCommand(self, cmd, cwd=cwd,
1945 capture_stdout=True,
1946 capture_stderr=True)
1947
1948 if command.Wait() != 0:
1949 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1950
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001951 def _RemoteFetch(self, name=None,
1952 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001953 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001954 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001955 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001956 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001957 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001958 depth=None,
1959 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001960
1961 is_sha1 = False
1962 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001963 # The depth should not be used when fetching to a mirror because
1964 # it will result in a shallow repository that cannot be cloned or
1965 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001966 # The repo project should also never be synced with partial depth.
1967 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1968 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001969
Shawn Pearce69e04d82014-01-29 12:48:54 -08001970 if depth:
1971 current_branch_only = True
1972
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001973 if ID_RE.match(self.revisionExpr) is not None:
1974 is_sha1 = True
1975
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001976 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001977 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001978 # this is a tag and its sha1 value should never change
1979 tag_name = self.revisionExpr[len(R_TAGS):]
1980
1981 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001982 if self._CheckForImmutableRevision():
1983 print('Skipped fetching project %s (already have persistent ref)'
1984 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001985 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001986 if is_sha1 and not depth:
1987 # When syncing a specific commit and --depth is not set:
1988 # * if upstream is explicitly specified and is not a sha1, fetch only
1989 # upstream as users expect only upstream to be fetch.
1990 # Note: The commit might not be in upstream in which case the sync
1991 # will fail.
1992 # * otherwise, fetch all branches to make sure we end up with the
1993 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001994 if self.upstream:
1995 current_branch_only = not ID_RE.match(self.upstream)
1996 else:
1997 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001998
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001999 if not name:
2000 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002001
2002 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002003 remote = self.GetRemote(name)
2004 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002005 ssh_proxy = True
2006
Shawn O. Pearce88443382010-10-08 10:02:09 +02002007 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002008 if alt_dir and 'objects' == os.path.basename(alt_dir):
2009 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002010 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2011 remote = self.GetRemote(name)
2012
David Pursehouse8a68ff92012-09-24 12:15:13 +09002013 all_refs = self.bare_ref.all
2014 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002015 tmp = set()
2016
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302017 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002018 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002019 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002020 all_refs[r] = ref_id
2021 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002022 continue
2023
David Pursehouse8a68ff92012-09-24 12:15:13 +09002024 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002025 continue
2026
David Pursehouse8a68ff92012-09-24 12:15:13 +09002027 r = 'refs/_alt/%s' % ref_id
2028 all_refs[r] = ref_id
2029 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002030 tmp.add(r)
2031
heping3d7bbc92017-04-12 19:51:47 +08002032 tmp_packed_lines = []
2033 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002034
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302035 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002036 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002037 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002039 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002040
heping3d7bbc92017-04-12 19:51:47 +08002041 tmp_packed = ''.join(tmp_packed_lines)
2042 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002043 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002044 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002045 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002046
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002047 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002048
Conley Owensf97e8382015-01-21 11:12:46 -08002049 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002050 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002051 else:
2052 # If this repo has shallow objects, then we don't know which refs have
2053 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2054 # do this with projects that don't have shallow objects, since it is less
2055 # efficient.
2056 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2057 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002058
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002059 if quiet:
2060 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002061 if not self.worktree:
2062 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002063 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002064
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002065 # If using depth then we should not get all the tags since they may
2066 # be outside of the depth.
2067 if no_tags or depth:
2068 cmd.append('--no-tags')
2069 else:
2070 cmd.append('--tags')
2071
David Pursehouse74cfd272015-10-14 10:50:15 +09002072 if prune:
2073 cmd.append('--prune')
2074
Martin Kellye4e94d22017-03-21 16:05:12 -07002075 if submodules:
2076 cmd.append('--recurse-submodules=on-demand')
2077
Conley Owens80b87fe2014-05-09 17:13:44 -07002078 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002079 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002080 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002081 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002082 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002083 spec.append('tag')
2084 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002085
David Pursehouse403b64e2015-04-27 10:41:33 +09002086 if not self.manifest.IsMirror:
2087 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002088 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002089 # Shallow checkout of a specific commit, fetch from that commit and not
2090 # the heads only as the commit might be deeper in the history.
2091 spec.append(branch)
2092 else:
2093 if is_sha1:
2094 branch = self.upstream
2095 if branch is not None and branch.strip():
2096 if not branch.startswith('refs/'):
2097 branch = R_HEADS + branch
2098 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002099 cmd.extend(spec)
2100
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002101 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002102 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002103 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002104 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002105 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002106 ok = True
2107 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002108 # If needed, run the 'git remote prune' the first time through the loop
2109 elif (not _i and
2110 "error:" in gitcmd.stderr and
2111 "git remote prune" in gitcmd.stderr):
2112 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002113 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002114 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002115 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002116 break
2117 continue
Brian Harring14a66742012-09-28 20:21:57 -07002118 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002119 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2120 # in sha1 mode, we just tried sync'ing from the upstream field; it
2121 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002122 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002123 elif ret < 0:
2124 # Git died with a signal, exit immediately
2125 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002126 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002127
2128 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002129 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002130 if old_packed != '':
2131 _lwrite(packed_refs, old_packed)
2132 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002133 platform_utils.remove(packed_refs)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002134 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002135
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002136 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002137 # We just synced the upstream given branch; verify we
2138 # got what we wanted, else trigger a second run of all
2139 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002140 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002141 if current_branch_only and depth:
2142 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002143 return self._RemoteFetch(name=name,
2144 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002145 initial=False, quiet=quiet, alt_dir=alt_dir,
2146 depth=None)
2147 else:
2148 # Avoid infinite recursion: sync all branches with depth set to None
2149 return self._RemoteFetch(name=name, current_branch_only=False,
2150 initial=False, quiet=quiet, alt_dir=alt_dir,
2151 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002152
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002153 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002154
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002155 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002156 if initial and \
2157 (self.manifest.manifestProject.config.GetString('repo.depth') or
2158 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002159 return False
2160
2161 remote = self.GetRemote(self.remote.name)
2162 bundle_url = remote.url + '/clone.bundle'
2163 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002164 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2165 'persistent-http',
2166 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002167 return False
2168
2169 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2170 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2171
2172 exist_dst = os.path.exists(bundle_dst)
2173 exist_tmp = os.path.exists(bundle_tmp)
2174
2175 if not initial and not exist_dst and not exist_tmp:
2176 return False
2177
2178 if not exist_dst:
2179 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2180 if not exist_dst:
2181 return False
2182
2183 cmd = ['fetch']
2184 if quiet:
2185 cmd.append('--quiet')
2186 if not self.worktree:
2187 cmd.append('--update-head-ok')
2188 cmd.append(bundle_dst)
2189 for f in remote.fetch:
2190 cmd.append(str(f))
2191 cmd.append('refs/tags/*:refs/tags/*')
2192
2193 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002194 if os.path.exists(bundle_dst):
Renaud Paquay010fed72016-11-11 14:25:29 -08002195 platform_utils.remove(bundle_dst)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002196 if os.path.exists(bundle_tmp):
Renaud Paquay010fed72016-11-11 14:25:29 -08002197 platform_utils.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002198 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002199
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002200 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002201 if os.path.exists(dstPath):
Renaud Paquay010fed72016-11-11 14:25:29 -08002202 platform_utils.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002203
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002204 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002205 if quiet:
2206 cmd += ['--silent']
2207 if os.path.exists(tmpPath):
2208 size = os.stat(tmpPath).st_size
2209 if size >= 1024:
2210 cmd += ['--continue-at', '%d' % (size,)]
2211 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002212 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002213 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2214 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002215 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002216 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002217 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002218 if srcUrl.startswith('persistent-'):
2219 srcUrl = srcUrl[len('persistent-'):]
2220 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002221
Dave Borowitz137d0132015-01-02 11:12:54 -08002222 if IsTrace():
2223 Trace('%s', ' '.join(cmd))
2224 try:
2225 proc = subprocess.Popen(cmd)
2226 except OSError:
2227 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002228
Dave Borowitz137d0132015-01-02 11:12:54 -08002229 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002230
Dave Borowitz137d0132015-01-02 11:12:54 -08002231 if curlret == 22:
2232 # From curl man page:
2233 # 22: HTTP page not retrieved. The requested url was not found or
2234 # returned another error with the HTTP error code being 400 or above.
2235 # This return code only appears if -f, --fail is used.
2236 if not quiet:
2237 print("Server does not provide clone.bundle; ignoring.",
2238 file=sys.stderr)
2239 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002240
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002241 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002242 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002243 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002244 return True
2245 else:
Renaud Paquay010fed72016-11-11 14:25:29 -08002246 platform_utils.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002247 return False
2248 else:
2249 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002250
Kris Giesingc8d882a2014-12-23 13:02:32 -08002251 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002252 try:
2253 with open(path) as f:
2254 if f.read(16) == '# v2 git bundle\n':
2255 return True
2256 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002257 if not quiet:
2258 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002259 return False
2260 except OSError:
2261 return False
2262
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002263 def _Checkout(self, rev, quiet=False):
2264 cmd = ['checkout']
2265 if quiet:
2266 cmd.append('-q')
2267 cmd.append(rev)
2268 cmd.append('--')
2269 if GitCommand(self, cmd).Wait() != 0:
2270 if self._allrefs:
2271 raise GitError('%s checkout %s ' % (self.name, rev))
2272
Anthony King7bdac712014-07-16 12:56:40 +01002273 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002274 cmd = ['cherry-pick']
2275 cmd.append(rev)
2276 cmd.append('--')
2277 if GitCommand(self, cmd).Wait() != 0:
2278 if self._allrefs:
2279 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2280
Akshay Verma0f2e45a2018-03-24 12:27:05 +05302281 def _LsRemote(self, refs):
2282 cmd = ['ls-remote', self.remote.name, refs]
Akshay Vermacf7c0832018-03-15 21:56:30 +05302283 p = GitCommand(self, cmd, capture_stdout=True)
2284 if p.Wait() == 0:
2285 if hasattr(p.stdout, 'decode'):
2286 return p.stdout.decode('utf-8')
2287 else:
2288 return p.stdout
2289 return None
2290
Anthony King7bdac712014-07-16 12:56:40 +01002291 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002292 cmd = ['revert']
2293 cmd.append('--no-edit')
2294 cmd.append(rev)
2295 cmd.append('--')
2296 if GitCommand(self, cmd).Wait() != 0:
2297 if self._allrefs:
2298 raise GitError('%s revert %s ' % (self.name, rev))
2299
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002300 def _ResetHard(self, rev, quiet=True):
2301 cmd = ['reset', '--hard']
2302 if quiet:
2303 cmd.append('-q')
2304 cmd.append(rev)
2305 if GitCommand(self, cmd).Wait() != 0:
2306 raise GitError('%s reset --hard %s ' % (self.name, rev))
2307
Martin Kellye4e94d22017-03-21 16:05:12 -07002308 def _SyncSubmodules(self, quiet=True):
2309 cmd = ['submodule', 'update', '--init', '--recursive']
2310 if quiet:
2311 cmd.append('-q')
2312 if GitCommand(self, cmd).Wait() != 0:
2313 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2314
Anthony King7bdac712014-07-16 12:56:40 +01002315 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002316 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002317 if onto is not None:
2318 cmd.extend(['--onto', onto])
2319 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002320 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002321 raise GitError('%s rebase %s ' % (self.name, upstream))
2322
Pierre Tardy3d125942012-05-04 12:18:12 +02002323 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002324 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002325 if ffonly:
2326 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002327 if GitCommand(self, cmd).Wait() != 0:
2328 raise GitError('%s merge %s ' % (self.name, head))
2329
Kevin Degiabaa7f32014-11-12 11:27:45 -07002330 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002331 init_git_dir = not os.path.exists(self.gitdir)
2332 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002333 try:
2334 # Initialize the bare repository, which contains all of the objects.
2335 if init_obj_dir:
2336 os.makedirs(self.objdir)
2337 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002338
Kevin Degib1a07b82015-07-27 13:33:43 -06002339 # If we have a separate directory to hold refs, initialize it as well.
2340 if self.objdir != self.gitdir:
2341 if init_git_dir:
2342 os.makedirs(self.gitdir)
2343
2344 if init_obj_dir or init_git_dir:
2345 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2346 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002347 try:
2348 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2349 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002350 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002351 print("Retrying clone after deleting %s" %
2352 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002353 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002354 platform_utils.rmtree(platform_utils.realpath(self.gitdir))
2355 if self.worktree and os.path.exists(platform_utils.realpath
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002356 (self.worktree)):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002357 platform_utils.rmtree(platform_utils.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002358 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2359 except:
2360 raise e
2361 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002362
Kevin Degi384b3c52014-10-16 16:02:58 -06002363 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002364 mp = self.manifest.manifestProject
2365 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002366
Kevin Degib1a07b82015-07-27 13:33:43 -06002367 if ref_dir or mirror_git:
2368 if not mirror_git:
2369 mirror_git = os.path.join(ref_dir, self.name + '.git')
2370 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2371 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002372
Kevin Degib1a07b82015-07-27 13:33:43 -06002373 if os.path.exists(mirror_git):
2374 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002375
Kevin Degib1a07b82015-07-27 13:33:43 -06002376 elif os.path.exists(repo_git):
2377 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002378
Kevin Degib1a07b82015-07-27 13:33:43 -06002379 else:
2380 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002381
Kevin Degib1a07b82015-07-27 13:33:43 -06002382 if ref_dir:
Samuel Hollandbaa00092018-01-22 10:57:29 -06002383 if not os.path.isabs(ref_dir):
2384 # The alternate directory is relative to the object database.
2385 ref_dir = os.path.relpath(ref_dir,
2386 os.path.join(self.objdir, 'objects'))
Kevin Degib1a07b82015-07-27 13:33:43 -06002387 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2388 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002389
Kevin Degib1a07b82015-07-27 13:33:43 -06002390 self._UpdateHooks()
2391
2392 m = self.manifest.manifestProject.config
2393 for key in ['user.name', 'user.email']:
2394 if m.Has(key, include_defaults=False):
2395 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002396 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002397 if self.manifest.IsMirror:
2398 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002399 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002400 self.config.SetString('core.bare', None)
2401 except Exception:
2402 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002403 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002404 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002405 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002406 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002407
Jimmie Westera0444582012-10-24 13:44:42 +02002408 def _UpdateHooks(self):
2409 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002410 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002411
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002412 def _InitHooks(self):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002413 hooks = platform_utils.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002414 if not os.path.exists(hooks):
2415 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002416 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002417 name = os.path.basename(stock_hook)
2418
Victor Boivie65e0f352011-04-18 11:23:29 +02002419 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002420 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002421 # Don't install a Gerrit Code Review hook if this
2422 # project does not appear to use it for reviews.
2423 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002424 # Since the manifest project is one of those, but also
2425 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002426 continue
2427
2428 dst = os.path.join(hooks, name)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002429 if platform_utils.islink(dst):
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002430 continue
2431 if os.path.exists(dst):
2432 if filecmp.cmp(stock_hook, dst, shallow=False):
Renaud Paquay010fed72016-11-11 14:25:29 -08002433 platform_utils.remove(dst)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002434 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002435 _warn("%s: Not replacing locally modified %s hook",
2436 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002437 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002438 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002439 platform_utils.symlink(
2440 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002441 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002442 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002443 raise GitError(self._get_symlink_error_message())
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002444 else:
2445 raise
2446
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002447 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002448 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002449 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002450 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002451 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002452 remote.review = self.remote.review
2453 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002454
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002455 if self.worktree:
2456 remote.ResetFetch(mirror=False)
2457 else:
2458 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002459 remote.Save()
2460
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002461 def _InitMRef(self):
2462 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002463 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002464
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002465 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002466 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002467
2468 def _InitAnyMRef(self, ref):
2469 cur = self.bare_ref.symref(ref)
2470
2471 if self.revisionId:
2472 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2473 msg = 'manifest set to %s' % self.revisionId
2474 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002475 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002476 else:
2477 remote = self.GetRemote(self.remote.name)
2478 dst = remote.ToLocal(self.revisionExpr)
2479 if cur != dst:
2480 msg = 'manifest set to %s' % self.revisionExpr
2481 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002482
Kevin Degi384b3c52014-10-16 16:02:58 -06002483 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002484 symlink_files = self.shareable_files[:]
2485 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002486 if share_refs:
2487 symlink_files += self.working_tree_files
2488 symlink_dirs += self.working_tree_dirs
2489 to_symlink = symlink_files + symlink_dirs
2490 for name in set(to_symlink):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002491 dst = platform_utils.realpath(os.path.join(destdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002492 if os.path.lexists(dst):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002493 src = platform_utils.realpath(os.path.join(srcdir, name))
Kevin Degi384b3c52014-10-16 16:02:58 -06002494 # Fail if the links are pointing to the wrong place
2495 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002496 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002497 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002498 'work tree. If you\'re comfortable with the '
2499 'possibility of losing the work tree\'s git metadata,'
2500 ' use `repo sync --force-sync {0}` to '
2501 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002502
David James8d201162013-10-11 17:03:19 -07002503 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2504 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2505
2506 Args:
2507 gitdir: The bare git repository. Must already be initialized.
2508 dotgit: The repository you would like to initialize.
2509 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2510 Only one work tree can store refs under a given |gitdir|.
2511 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2512 This saves you the effort of initializing |dotgit| yourself.
2513 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002514 symlink_files = self.shareable_files[:]
2515 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002516 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002517 symlink_files += self.working_tree_files
2518 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002519 to_symlink = symlink_files + symlink_dirs
2520
2521 to_copy = []
2522 if copy_all:
2523 to_copy = os.listdir(gitdir)
2524
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002525 dotgit = platform_utils.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002526 for name in set(to_copy).union(to_symlink):
2527 try:
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002528 src = platform_utils.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002529 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002530
Kevin Degi384b3c52014-10-16 16:02:58 -06002531 if os.path.lexists(dst):
2532 continue
David James8d201162013-10-11 17:03:19 -07002533
2534 # If the source dir doesn't exist, create an empty dir.
2535 if name in symlink_dirs and not os.path.lexists(src):
2536 os.makedirs(src)
2537
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002538 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002539 platform_utils.symlink(
2540 os.path.relpath(src, os.path.dirname(dst)), dst)
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002541 elif copy_all and not platform_utils.islink(dst):
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002542 if os.path.isdir(src):
2543 shutil.copytree(src, dst)
2544 elif os.path.isfile(src):
2545 shutil.copy(src, dst)
2546
Conley Owens80b87fe2014-05-09 17:13:44 -07002547 # If the source file doesn't exist, ensure the destination
2548 # file doesn't either.
2549 if name in symlink_files and not os.path.lexists(src):
2550 try:
Renaud Paquay010fed72016-11-11 14:25:29 -08002551 platform_utils.remove(dst)
Conley Owens80b87fe2014-05-09 17:13:44 -07002552 except OSError:
2553 pass
2554
David James8d201162013-10-11 17:03:19 -07002555 except OSError as e:
2556 if e.errno == errno.EPERM:
Renaud Paquay788e9622017-01-27 11:41:12 -08002557 raise DownloadError(self._get_symlink_error_message())
David James8d201162013-10-11 17:03:19 -07002558 else:
2559 raise
2560
Martin Kellye4e94d22017-03-21 16:05:12 -07002561 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002562 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002563 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002564 try:
2565 if init_dotgit:
2566 os.makedirs(dotgit)
2567 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2568 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002569
Kevin Degiabaa7f32014-11-12 11:27:45 -07002570 try:
2571 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2572 except GitError as e:
2573 if force_sync:
2574 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002575 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002576 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002577 except:
2578 raise e
2579 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002580
Kevin Degib1a07b82015-07-27 13:33:43 -06002581 if init_dotgit:
2582 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002583
Kevin Degib1a07b82015-07-27 13:33:43 -06002584 cmd = ['read-tree', '--reset', '-u']
2585 cmd.append('-v')
2586 cmd.append(HEAD)
2587 if GitCommand(self, cmd).Wait() != 0:
2588 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002589
Martin Kellye4e94d22017-03-21 16:05:12 -07002590 if submodules:
2591 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002592 self._CopyAndLinkFiles()
2593 except Exception:
2594 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002595 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002596 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002597
Renaud Paquay788e9622017-01-27 11:41:12 -08002598 def _get_symlink_error_message(self):
2599 if platform_utils.isWindows():
2600 return ('Unable to create symbolic link. Please re-run the command as '
2601 'Administrator, or see '
2602 'https://github.com/git-for-windows/git/wiki/Symbolic-Links '
2603 'for other options.')
2604 return 'filesystem must support symlinks'
2605
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002606 def _gitdir_path(self, path):
Renaud Paquay227ad2e2016-11-01 14:37:13 -07002607 return platform_utils.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002608
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002609 def _revlist(self, *args, **kw):
2610 a = []
2611 a.extend(args)
2612 a.append('--')
2613 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002614
2615 @property
2616 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002617 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002618
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002619 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002620 """Get logs between two revisions of this project."""
2621 comp = '..'
2622 if rev1:
2623 revs = [rev1]
2624 if rev2:
2625 revs.extend([comp, rev2])
2626 cmd = ['log', ''.join(revs)]
2627 out = DiffColoring(self.config)
2628 if out.is_on and color:
2629 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002630 if pretty_format is not None:
2631 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002632 if oneline:
2633 cmd.append('--oneline')
2634
2635 try:
2636 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2637 if log.Wait() == 0:
2638 return log.stdout
2639 except GitError:
2640 # worktree may not exist if groups changed for example. In that case,
2641 # try in gitdir instead.
2642 if not os.path.exists(self.worktree):
2643 return self.bare_git.log(*cmd[1:])
2644 else:
2645 raise
2646 return None
2647
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002648 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2649 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002650 """Get the list of logs from this revision to given revisionId"""
2651 logs = {}
2652 selfId = self.GetRevisionId(self._allrefs)
2653 toId = toProject.GetRevisionId(toProject._allrefs)
2654
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002655 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2656 pretty_format=pretty_format)
2657 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2658 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002659 return logs
2660
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002661 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002662
David James8d201162013-10-11 17:03:19 -07002663 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002664 self._project = project
2665 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002666 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002667
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002668 def LsOthers(self):
2669 p = GitCommand(self._project,
2670 ['ls-files',
2671 '-z',
2672 '--others',
2673 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002674 bare=False,
David James8d201162013-10-11 17:03:19 -07002675 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002676 capture_stdout=True,
2677 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002678 if p.Wait() == 0:
2679 out = p.stdout
2680 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002681 # Backslash is not anomalous
David Pursehouse65b0ba52018-06-24 16:21:51 +09002682 return out[:-1].split('\0')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002683 return []
2684
2685 def DiffZ(self, name, *args):
2686 cmd = [name]
2687 cmd.append('-z')
2688 cmd.extend(args)
2689 p = GitCommand(self._project,
2690 cmd,
David James8d201162013-10-11 17:03:19 -07002691 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002692 bare=False,
2693 capture_stdout=True,
2694 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002695 try:
2696 out = p.process.stdout.read()
2697 r = {}
2698 if out:
David Pursehouse65b0ba52018-06-24 16:21:51 +09002699 out = iter(out[:-1].split('\0'))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002700 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002701 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002702 info = next(out)
2703 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002704 except StopIteration:
2705 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002706
2707 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002708
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002709 def __init__(self, path, omode, nmode, oid, nid, state):
2710 self.path = path
2711 self.src_path = None
2712 self.old_mode = omode
2713 self.new_mode = nmode
2714 self.old_id = oid
2715 self.new_id = nid
2716
2717 if len(state) == 1:
2718 self.status = state
2719 self.level = None
2720 else:
2721 self.status = state[:1]
2722 self.level = state[1:]
2723 while self.level.startswith('0'):
2724 self.level = self.level[1:]
2725
2726 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002727 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002728 if info.status in ('R', 'C'):
2729 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002730 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002731 r[info.path] = info
2732 return r
2733 finally:
2734 p.Wait()
2735
2736 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002737 if self._bare:
2738 path = os.path.join(self._project.gitdir, HEAD)
2739 else:
2740 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002741 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002742 fd = open(path)
Dan Sandler53e902a2014-03-09 13:20:02 -04002743 except IOError as e:
2744 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002745 try:
Renaud Paquay2a4be942016-11-01 13:48:15 -07002746 line = fd.readline()
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002747 finally:
2748 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302749 try:
2750 line = line.decode()
2751 except AttributeError:
2752 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002753 if line.startswith('ref: '):
2754 return line[5:-1]
2755 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002756
2757 def SetHead(self, ref, message=None):
2758 cmdv = []
2759 if message is not None:
2760 cmdv.extend(['-m', message])
2761 cmdv.append(HEAD)
2762 cmdv.append(ref)
2763 self.symbolic_ref(*cmdv)
2764
2765 def DetachHead(self, new, message=None):
2766 cmdv = ['--no-deref']
2767 if message is not None:
2768 cmdv.extend(['-m', message])
2769 cmdv.append(HEAD)
2770 cmdv.append(new)
2771 self.update_ref(*cmdv)
2772
2773 def UpdateRef(self, name, new, old=None,
2774 message=None,
2775 detach=False):
2776 cmdv = []
2777 if message is not None:
2778 cmdv.extend(['-m', message])
2779 if detach:
2780 cmdv.append('--no-deref')
2781 cmdv.append(name)
2782 cmdv.append(new)
2783 if old is not None:
2784 cmdv.append(old)
2785 self.update_ref(*cmdv)
2786
2787 def DeleteRef(self, name, old=None):
2788 if not old:
2789 old = self.rev_parse(name)
2790 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002791 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002792
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002793 def rev_list(self, *args, **kw):
2794 if 'format' in kw:
2795 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2796 else:
2797 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002798 cmdv.extend(args)
2799 p = GitCommand(self._project,
2800 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002801 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002802 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002803 capture_stdout=True,
2804 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002805 r = []
2806 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002807 if line[-1] == '\n':
2808 line = line[:-1]
2809 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002810 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002811 raise GitError('%s rev-list %s: %s' %
2812 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002813 return r
2814
2815 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002816 """Allow arbitrary git commands using pythonic syntax.
2817
2818 This allows you to do things like:
2819 git_obj.rev_parse('HEAD')
2820
2821 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2822 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002823 Any other positional arguments will be passed to the git command, and the
2824 following keyword arguments are supported:
2825 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002826
2827 Args:
2828 name: The name of the git command to call. Any '_' characters will
2829 be replaced with '-'.
2830
2831 Returns:
2832 A callable object that will try to call git with the named command.
2833 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002834 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002835
Dave Borowitz091f8932012-10-23 17:01:04 -07002836 def runner(*args, **kwargs):
2837 cmdv = []
2838 config = kwargs.pop('config', None)
2839 for k in kwargs:
2840 raise TypeError('%s() got an unexpected keyword argument %r'
2841 % (name, k))
2842 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002843 if not git_require((1, 7, 2)):
2844 raise ValueError('cannot set config on command line for %s()'
2845 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302846 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002847 cmdv.append('-c')
2848 cmdv.append('%s=%s' % (k, v))
2849 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002850 cmdv.extend(args)
2851 p = GitCommand(self._project,
2852 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002853 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002854 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002855 capture_stdout=True,
2856 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002857 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002858 raise GitError('%s %s: %s' %
2859 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002860 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302861 try:
Conley Owensedd01512013-09-26 12:59:58 -07002862 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302863 except AttributeError:
2864 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002865 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2866 return r[:-1]
2867 return r
2868 return runner
2869
2870
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002872
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002873 def __str__(self):
2874 return 'prior sync failed; rebase still in progress'
2875
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002876
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002877class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002878
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002879 def __str__(self):
2880 return 'contains uncommitted changes'
2881
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002882
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002883class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002884
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002885 def __init__(self, project, text):
2886 self.project = project
2887 self.text = text
2888
2889 def Print(self, syncbuf):
2890 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2891 syncbuf.out.nl()
2892
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002893
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002894class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002895
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002896 def __init__(self, project, why):
2897 self.project = project
2898 self.why = why
2899
2900 def Print(self, syncbuf):
2901 syncbuf.out.fail('error: %s/: %s',
2902 self.project.relpath,
2903 str(self.why))
2904 syncbuf.out.nl()
2905
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002906
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002907class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002908
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002909 def __init__(self, project, action):
2910 self.project = project
2911 self.action = action
2912
2913 def Run(self, syncbuf):
2914 out = syncbuf.out
2915 out.project('project %s/', self.project.relpath)
2916 out.nl()
2917 try:
2918 self.action()
2919 out.nl()
2920 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002921 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002922 out.nl()
2923 return False
2924
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002925
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002926class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002927
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002928 def __init__(self, config):
2929 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002930 self.project = self.printer('header', attr='bold')
2931 self.info = self.printer('info')
2932 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002933
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002934
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002935class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002936
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002937 def __init__(self, config, detach_head=False):
2938 self._messages = []
2939 self._failures = []
2940 self._later_queue1 = []
2941 self._later_queue2 = []
2942
2943 self.out = _SyncColoring(config)
2944 self.out.redirect(sys.stderr)
2945
2946 self.detach_head = detach_head
2947 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002948 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002949
2950 def info(self, project, fmt, *args):
2951 self._messages.append(_InfoMessage(project, fmt % args))
2952
2953 def fail(self, project, err=None):
2954 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002955 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002956
2957 def later1(self, project, what):
2958 self._later_queue1.append(_Later(project, what))
2959
2960 def later2(self, project, what):
2961 self._later_queue2.append(_Later(project, what))
2962
2963 def Finish(self):
2964 self._PrintMessages()
2965 self._RunLater()
2966 self._PrintMessages()
2967 return self.clean
2968
David Rileye0684ad2017-04-05 00:02:59 -07002969 def Recently(self):
2970 recent_clean = self.recent_clean
2971 self.recent_clean = True
2972 return recent_clean
2973
2974 def _MarkUnclean(self):
2975 self.clean = False
2976 self.recent_clean = False
2977
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002978 def _RunLater(self):
2979 for q in ['_later_queue1', '_later_queue2']:
2980 if not self._RunQueue(q):
2981 return
2982
2983 def _RunQueue(self, queue):
2984 for m in getattr(self, queue):
2985 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002986 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002987 return False
2988 setattr(self, queue, [])
2989 return True
2990
2991 def _PrintMessages(self):
2992 for m in self._messages:
2993 m.Print(self)
2994 for m in self._failures:
2995 m.Print(self)
2996
2997 self._messages = []
2998 self._failures = []
2999
3000
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003001class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003002
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003003 """A special project housed under .repo.
3004 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003005
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003006 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003007 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01003008 manifest=manifest,
3009 name=name,
3010 gitdir=gitdir,
3011 objdir=gitdir,
3012 worktree=worktree,
3013 remote=RemoteSpec('origin'),
3014 relpath='.repo/%s' % name,
3015 revisionExpr='refs/heads/master',
3016 revisionId=None,
3017 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003018
3019 def PreSync(self):
3020 if self.Exists:
3021 cb = self.CurrentBranch
3022 if cb:
3023 base = self.GetBranch(cb).merge
3024 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003025 self.revisionExpr = base
3026 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003027
Martin Kelly224a31a2017-07-10 14:46:25 -07003028 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02003029 """ Prepare MetaProject for manifest branch switch
3030 """
3031
3032 # detach and delete manifest branch, allowing a new
3033 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003034 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07003035 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02003036 syncbuf.Finish()
3037
3038 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003039 ['update-ref', '-d', 'refs/heads/default'],
3040 capture_stdout=True,
3041 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003042
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003043 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003044 def LastFetch(self):
3045 try:
3046 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3047 return os.path.getmtime(fh)
3048 except OSError:
3049 return 0
3050
3051 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003052 def HasChanges(self):
3053 """Has the remote received new commits not yet checked out?
3054 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003055 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003056 return False
3057
David Pursehouse8a68ff92012-09-24 12:15:13 +09003058 all_refs = self.bare_ref.all
3059 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003060 head = self.work_git.GetHead()
3061 if head.startswith(R_HEADS):
3062 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003063 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003064 except KeyError:
3065 head = None
3066
3067 if revid == head:
3068 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003069 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003070 return True
3071 return False