blob: e700d16a9473f4f00edc4c95ee19b6cbec9cde80 [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080036from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080037from error import NoManifestException
Renaud Paquayd5cec5e2016-11-01 11:24:03 -070038import platform_utils
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070039from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Shawn O. Pearced237b692009-04-17 18:49:50 -070041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
David Pursehouse59bbb582013-05-17 10:49:33 +090043from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040044if is_python3():
45 import urllib.parse
46else:
47 import imp
48 import urlparse
49 urllib = imp.new_module('urllib')
50 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090051 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090053 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
Renaud Paquayad1abcb2016-11-01 11:34:55 -070066 platform_utils.rename(lock, path)
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070067 except OSError:
68 os.remove(lock)
69 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
106 d = os.path.realpath(os.path.abspath(os.path.dirname(__file__)))
107 d = os.path.join(d, 'hooks')
108 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000179 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200180 private=False,
181 wip=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700182 dest_branch=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800183 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700184 people,
Brian Harring435370c2012-07-28 15:37:04 -0700185 auto_topic=auto_topic,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +0000186 draft=draft,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200187 private=private,
188 wip=wip,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400189 dest_branch=dest_branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700190
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700191 def GetPublishedRefs(self):
192 refs = {}
193 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700194 self.branch.remote.SshReviewUrl(self.project.UserEmail),
195 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700196 for line in output.split('\n'):
197 try:
198 (sha, ref) = line.split()
199 refs[sha] = ref
200 except ValueError:
201 pass
202
203 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700204
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700205
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700206class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700207
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208 def __init__(self, config):
209 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100210 self.project = self.printer('header', attr='bold')
211 self.branch = self.printer('header', attr='bold')
212 self.nobranch = self.printer('nobranch', fg='red')
213 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700214
Anthony King7bdac712014-07-16 12:56:40 +0100215 self.added = self.printer('added', fg='green')
216 self.changed = self.printer('changed', fg='red')
217 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700218
219
220class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700221
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222 def __init__(self, config):
223 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100224 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700225
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700226
Anthony King7bdac712014-07-16 12:56:40 +0100227class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
James W. Mills24c13082012-04-12 15:04:13 -0500229 def __init__(self, name, value, keep):
230 self.name = name
231 self.value = value
232 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700234
Anthony King7bdac712014-07-16 12:56:40 +0100235class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800237 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700238 self.src = src
239 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800240 self.abs_src = abssrc
241 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700242
243 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800244 src = self.abs_src
245 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700246 # copy file if it does not exist or is out of date
247 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
248 try:
249 # remove existing file first, since it might be read-only
250 if os.path.exists(dest):
251 os.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400252 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200253 dest_dir = os.path.dirname(dest)
254 if not os.path.isdir(dest_dir):
255 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700256 shutil.copy(src, dest)
257 # make the file read-only
258 mode = os.stat(dest)[stat.ST_MODE]
259 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
260 os.chmod(dest, mode)
261 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700262 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700263
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700264
Anthony King7bdac712014-07-16 12:56:40 +0100265class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700266
Wink Saville4c426ef2015-06-03 08:05:17 -0700267 def __init__(self, git_worktree, src, dest, relsrc, absdest):
268 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500269 self.src = src
270 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700271 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500272 self.abs_dest = absdest
273
Wink Saville4c426ef2015-06-03 08:05:17 -0700274 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500275 # link file if it does not exist or is out of date
Wink Saville4c426ef2015-06-03 08:05:17 -0700276 if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 try:
278 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800279 if os.path.lexists(absDest):
Wink Saville4c426ef2015-06-03 08:05:17 -0700280 os.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 if not os.path.isdir(dest_dir):
284 os.makedirs(dest_dir)
Renaud Paquayd5cec5e2016-11-01 11:24:03 -0700285 platform_utils.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500286 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 _error('Cannot link file %s to %s', relSrc, absDest)
288
289 def _Link(self):
290 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
291 on the src linking all of the files in the source in to the destination
292 directory.
293 """
294 # We use the absSrc to handle the situation where the current directory
295 # is not the root of the repo
296 absSrc = os.path.join(self.git_worktree, self.src)
297 if os.path.exists(absSrc):
298 # Entity exists so just a simple one to one link operation
299 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
300 else:
301 # Entity doesn't exist assume there is a wild card
302 absDestDir = self.abs_dest
303 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
304 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700305 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700306 else:
307 absSrcFiles = glob.glob(absSrc)
308 for absSrcFile in absSrcFiles:
309 # Create a releative path from source dir to destination dir
310 absSrcDir = os.path.dirname(absSrcFile)
311 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
312
313 # Get the source file name
314 srcFile = os.path.basename(absSrcFile)
315
316 # Now form the final full paths to srcFile. They will be
317 # absolute for the desintaiton and relative for the srouce.
318 absDest = os.path.join(absDestDir, srcFile)
319 relSrc = os.path.join(relSrcDir, srcFile)
320 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500321
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700322
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700323class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700324
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700325 def __init__(self,
326 name,
Anthony King7bdac712014-07-16 12:56:40 +0100327 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700328 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100329 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700330 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700331 orig_name=None,
332 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700333 self.name = name
334 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700335 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700336 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100337 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700338 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700339 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700340
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700341
Doug Anderson37282b42011-03-04 11:54:18 -0800342class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700343
Doug Anderson37282b42011-03-04 11:54:18 -0800344 """A RepoHook contains information about a script to run as a hook.
345
346 Hooks are used to run a python script before running an upload (for instance,
347 to run presubmit checks). Eventually, we may have hooks for other actions.
348
349 This shouldn't be confused with files in the 'repo/hooks' directory. Those
350 files are copied into each '.git/hooks' folder for each project. Repo-level
351 hooks are associated instead with repo actions.
352
353 Hooks are always python. When a hook is run, we will load the hook into the
354 interpreter and execute its main() function.
355 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700356
Doug Anderson37282b42011-03-04 11:54:18 -0800357 def __init__(self,
358 hook_type,
359 hooks_project,
360 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400361 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800362 abort_if_user_denies=False):
363 """RepoHook constructor.
364
365 Params:
366 hook_type: A string representing the type of hook. This is also used
367 to figure out the name of the file containing the hook. For
368 example: 'pre-upload'.
369 hooks_project: The project containing the repo hooks. If you have a
370 manifest, this is manifest.repo_hooks_project. OK if this is None,
371 which will make the hook a no-op.
372 topdir: Repo's top directory (the one containing the .repo directory).
373 Scripts will run with CWD as this directory. If you have a manifest,
374 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400375 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800376 abort_if_user_denies: If True, we'll throw a HookError() if the user
377 doesn't allow us to run the hook.
378 """
379 self._hook_type = hook_type
380 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400381 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800382 self._topdir = topdir
383 self._abort_if_user_denies = abort_if_user_denies
384
385 # Store the full path to the script for convenience.
386 if self._hooks_project:
387 self._script_fullpath = os.path.join(self._hooks_project.worktree,
388 self._hook_type + '.py')
389 else:
390 self._script_fullpath = None
391
392 def _GetHash(self):
393 """Return a hash of the contents of the hooks directory.
394
395 We'll just use git to do this. This hash has the property that if anything
396 changes in the directory we will return a different has.
397
398 SECURITY CONSIDERATION:
399 This hash only represents the contents of files in the hook directory, not
400 any other files imported or called by hooks. Changes to imported files
401 can change the script behavior without affecting the hash.
402
403 Returns:
404 A string representing the hash. This will always be ASCII so that it can
405 be printed to the user easily.
406 """
407 assert self._hooks_project, "Must have hooks to calculate their hash."
408
409 # We will use the work_git object rather than just calling GetRevisionId().
410 # That gives us a hash of the latest checked in version of the files that
411 # the user will actually be executing. Specifically, GetRevisionId()
412 # doesn't appear to change even if a user checks out a different version
413 # of the hooks repo (via git checkout) nor if a user commits their own revs.
414 #
415 # NOTE: Local (non-committed) changes will not be factored into this hash.
416 # I think this is OK, since we're really only worried about warning the user
417 # about upstream changes.
418 return self._hooks_project.work_git.rev_parse('HEAD')
419
420 def _GetMustVerb(self):
421 """Return 'must' if the hook is required; 'should' if not."""
422 if self._abort_if_user_denies:
423 return 'must'
424 else:
425 return 'should'
426
427 def _CheckForHookApproval(self):
428 """Check to see whether this hook has been approved.
429
Mike Frysinger40252c22016-08-15 21:23:44 -0400430 We'll accept approval of manifest URLs if they're using secure transports.
431 This way the user can say they trust the manifest hoster. For insecure
432 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800433
434 Note that we ask permission for each individual hook even though we use
435 the hash of all hooks when detecting changes. We'd like the user to be
436 able to approve / deny each hook individually. We only use the hash of all
437 hooks because there is no other easy way to detect changes to local imports.
438
439 Returns:
440 True if this hook is approved to run; False otherwise.
441
442 Raises:
443 HookError: Raised if the user doesn't approve and abort_if_user_denies
444 was passed to the consturctor.
445 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400446 if self._ManifestUrlHasSecureScheme():
447 return self._CheckForHookApprovalManifest()
448 else:
449 return self._CheckForHookApprovalHash()
450
451 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
452 changed_prompt):
453 """Check for approval for a particular attribute and hook.
454
455 Args:
456 subkey: The git config key under [repo.hooks.<hook_type>] to store the
457 last approved string.
458 new_val: The new value to compare against the last approved one.
459 main_prompt: Message to display to the user to ask for approval.
460 changed_prompt: Message explaining why we're re-asking for approval.
461
462 Returns:
463 True if this hook is approved to run; False otherwise.
464
465 Raises:
466 HookError: Raised if the user doesn't approve and abort_if_user_denies
467 was passed to the consturctor.
468 """
Doug Anderson37282b42011-03-04 11:54:18 -0800469 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400470 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800471
Mike Frysinger40252c22016-08-15 21:23:44 -0400472 # Get the last value that the user approved for this hook; may be None.
473 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800474
Mike Frysinger40252c22016-08-15 21:23:44 -0400475 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800476 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400477 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800478 # Approval matched. We're done.
479 return True
480 else:
481 # Give the user a reason why we're prompting, since they last told
482 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400483 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800484 else:
485 prompt = ''
486
487 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
488 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400489 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530490 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900491 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800492
493 # User is doing a one-time approval.
494 if response in ('y', 'yes'):
495 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400496 elif response == 'always':
497 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800498 return True
499
500 # For anything else, we'll assume no approval.
501 if self._abort_if_user_denies:
502 raise HookError('You must allow the %s hook or use --no-verify.' %
503 self._hook_type)
504
505 return False
506
Mike Frysinger40252c22016-08-15 21:23:44 -0400507 def _ManifestUrlHasSecureScheme(self):
508 """Check if the URI for the manifest is a secure transport."""
509 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
510 parse_results = urllib.parse.urlparse(self._manifest_url)
511 return parse_results.scheme in secure_schemes
512
513 def _CheckForHookApprovalManifest(self):
514 """Check whether the user has approved this manifest host.
515
516 Returns:
517 True if this hook is approved to run; False otherwise.
518 """
519 return self._CheckForHookApprovalHelper(
520 'approvedmanifest',
521 self._manifest_url,
522 'Run hook scripts from %s' % (self._manifest_url,),
523 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
524
525 def _CheckForHookApprovalHash(self):
526 """Check whether the user has approved the hooks repo.
527
528 Returns:
529 True if this hook is approved to run; False otherwise.
530 """
531 prompt = ('Repo %s run the script:\n'
532 ' %s\n'
533 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700534 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400535 return self._CheckForHookApprovalHelper(
536 'approvedhash',
537 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700538 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400539 'Scripts have changed since %s was allowed.' % (self._hook_type,))
540
Doug Anderson37282b42011-03-04 11:54:18 -0800541 def _ExecuteHook(self, **kwargs):
542 """Actually execute the given hook.
543
544 This will run the hook's 'main' function in our python interpreter.
545
546 Args:
547 kwargs: Keyword arguments to pass to the hook. These are often specific
548 to the hook type. For instance, pre-upload hooks will contain
549 a project_list.
550 """
551 # Keep sys.path and CWD stashed away so that we can always restore them
552 # upon function exit.
553 orig_path = os.getcwd()
554 orig_syspath = sys.path
555
556 try:
557 # Always run hooks with CWD as topdir.
558 os.chdir(self._topdir)
559
560 # Put the hook dir as the first item of sys.path so hooks can do
561 # relative imports. We want to replace the repo dir as [0] so
562 # hooks can't import repo files.
563 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
564
565 # Exec, storing global context in the context dict. We catch exceptions
566 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500567 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800568 try:
Anthony King70f68902014-05-05 21:15:34 +0100569 exec(compile(open(self._script_fullpath).read(),
570 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800571 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700572 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
573 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800574
575 # Running the script should have defined a main() function.
576 if 'main' not in context:
577 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
578
Doug Anderson37282b42011-03-04 11:54:18 -0800579 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
580 # We don't actually want hooks to define their main with this argument--
581 # it's there to remind them that their hook should always take **kwargs.
582 # For instance, a pre-upload hook should be defined like:
583 # def main(project_list, **kwargs):
584 #
585 # This allows us to later expand the API without breaking old hooks.
586 kwargs = kwargs.copy()
587 kwargs['hook_should_take_kwargs'] = True
588
589 # Call the main function in the hook. If the hook should cause the
590 # build to fail, it will raise an Exception. We'll catch that convert
591 # to a HookError w/ just the failing traceback.
592 try:
593 context['main'](**kwargs)
594 except Exception:
595 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700596 'above.' % (traceback.format_exc(),
597 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800598 finally:
599 # Restore sys.path and CWD.
600 sys.path = orig_syspath
601 os.chdir(orig_path)
602
603 def Run(self, user_allows_all_hooks, **kwargs):
604 """Run the hook.
605
606 If the hook doesn't exist (because there is no hooks project or because
607 this particular hook is not enabled), this is a no-op.
608
609 Args:
610 user_allows_all_hooks: If True, we will never prompt about running the
611 hook--we'll just assume it's OK to run it.
612 kwargs: Keyword arguments to pass to the hook. These are often specific
613 to the hook type. For instance, pre-upload hooks will contain
614 a project_list.
615
616 Raises:
617 HookError: If there was a problem finding the hook or the user declined
618 to run a required hook (from _CheckForHookApproval).
619 """
620 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700621 if ((not self._hooks_project) or (self._hook_type not in
622 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800623 return
624
625 # Bail with a nice error if we can't find the hook.
626 if not os.path.isfile(self._script_fullpath):
627 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
628
629 # Make sure the user is OK with running the hook.
630 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
631 return
632
633 # Run the hook with the same version of python we're using.
634 self._ExecuteHook(**kwargs)
635
636
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700637class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600638 # These objects can be shared between several working trees.
639 shareable_files = ['description', 'info']
640 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
641 # These objects can only be used by a single working tree.
642 working_tree_files = ['config', 'packed-refs', 'shallow']
643 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700644
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700645 def __init__(self,
646 manifest,
647 name,
648 remote,
649 gitdir,
David James8d201162013-10-11 17:03:19 -0700650 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700651 worktree,
652 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700653 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800654 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100655 rebase=True,
656 groups=None,
657 sync_c=False,
658 sync_s=False,
659 clone_depth=None,
660 upstream=None,
661 parent=None,
662 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900663 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700664 optimized_fetch=False,
665 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800666 """Init a Project object.
667
668 Args:
669 manifest: The XmlManifest object.
670 name: The `name` attribute of manifest.xml's project element.
671 remote: RemoteSpec object specifying its remote's properties.
672 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700673 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800674 worktree: Absolute path of git working tree.
675 relpath: Relative path of git working tree to repo's top directory.
676 revisionExpr: The `revision` attribute of manifest.xml's project element.
677 revisionId: git commit id for checking out.
678 rebase: The `rebase` attribute of manifest.xml's project element.
679 groups: The `groups` attribute of manifest.xml's project element.
680 sync_c: The `sync-c` attribute of manifest.xml's project element.
681 sync_s: The `sync-s` attribute of manifest.xml's project element.
682 upstream: The `upstream` attribute of manifest.xml's project element.
683 parent: The parent Project object.
684 is_derived: False if the project was explicitly defined in the manifest;
685 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400686 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900687 optimized_fetch: If True, when a project is set to a sha1 revision, only
688 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700689 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800690 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700691 self.manifest = manifest
692 self.name = name
693 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800694 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700695 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800696 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700697 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800698 else:
699 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700701 self.revisionExpr = revisionExpr
702
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700703 if revisionId is None \
704 and revisionExpr \
705 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700706 self.revisionId = revisionExpr
707 else:
708 self.revisionId = revisionId
709
Mike Pontillod3153822012-02-28 11:53:24 -0800710 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700711 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700712 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800713 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900714 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700715 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800716 self.parent = parent
717 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900718 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800720
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700721 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700722 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500723 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500724 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700725 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
726 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800728 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700729 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800730 else:
731 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700732 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700733 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700734 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400735 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700736 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737
Doug Anderson37282b42011-03-04 11:54:18 -0800738 # This will be filled in if a project is later identified to be the
739 # project containing repo hooks.
740 self.enabled_repo_hooks = []
741
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800743 def Derived(self):
744 return self.is_derived
745
746 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600748 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749
750 @property
751 def CurrentBranch(self):
752 """Obtain the name of the currently checked out branch.
753 The branch name omits the 'refs/heads/' prefix.
754 None is returned if the project is on a detached HEAD.
755 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700756 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 if b.startswith(R_HEADS):
758 return b[len(R_HEADS):]
759 return None
760
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700761 def IsRebaseInProgress(self):
762 w = self.worktree
763 g = os.path.join(w, '.git')
764 return os.path.exists(os.path.join(g, 'rebase-apply')) \
765 or os.path.exists(os.path.join(g, 'rebase-merge')) \
766 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200767
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700768 def IsDirty(self, consider_untracked=True):
769 """Is the working directory modified in some way?
770 """
771 self.work_git.update_index('-q',
772 '--unmerged',
773 '--ignore-missing',
774 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900775 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 return True
777 if self.work_git.DiffZ('diff-files'):
778 return True
779 if consider_untracked and self.work_git.LsOthers():
780 return True
781 return False
782
783 _userident_name = None
784 _userident_email = None
785
786 @property
787 def UserName(self):
788 """Obtain the user's personal name.
789 """
790 if self._userident_name is None:
791 self._LoadUserIdentity()
792 return self._userident_name
793
794 @property
795 def UserEmail(self):
796 """Obtain the user's email address. This is very likely
797 to be their Gerrit login.
798 """
799 if self._userident_email is None:
800 self._LoadUserIdentity()
801 return self._userident_email
802
803 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900804 u = self.bare_git.var('GIT_COMMITTER_IDENT')
805 m = re.compile("^(.*) <([^>]*)> ").match(u)
806 if m:
807 self._userident_name = m.group(1)
808 self._userident_email = m.group(2)
809 else:
810 self._userident_name = ''
811 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812
813 def GetRemote(self, name):
814 """Get the configuration for a single remote.
815 """
816 return self.config.GetRemote(name)
817
818 def GetBranch(self, name):
819 """Get the configuration for a single branch.
820 """
821 return self.config.GetBranch(name)
822
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700823 def GetBranches(self):
824 """Get all existing local branches.
825 """
826 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900827 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700829
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530830 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 if name.startswith(R_HEADS):
832 name = name[len(R_HEADS):]
833 b = self.GetBranch(name)
834 b.current = name == current
835 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900836 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837 heads[name] = b
838
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530839 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700840 if name.startswith(R_PUB):
841 name = name[len(R_PUB):]
842 b = heads.get(name)
843 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845
846 return heads
847
Colin Cross5acde752012-03-28 20:15:45 -0700848 def MatchesGroups(self, manifest_groups):
849 """Returns true if the manifest groups specified at init should cause
850 this project to be synced.
851 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700852 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700853
854 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700855 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700856 manifest_groups: "-group1,group2"
857 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500858
859 The special manifest group "default" will match any project that
860 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700861 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500862 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700864 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500865 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700866
Conley Owens971de8e2012-04-16 10:36:08 -0700867 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 for group in expanded_manifest_groups:
869 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700870 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700872 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700873
Conley Owens971de8e2012-04-16 10:36:08 -0700874 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700876# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700877 def UncommitedFiles(self, get_all=True):
878 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700880 Args:
881 get_all: a boolean, if True - get information about all different
882 uncommitted files. If False - return as soon as any kind of
883 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500884 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500886 self.work_git.update_index('-q',
887 '--unmerged',
888 '--ignore-missing',
889 '--refresh')
890 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700891 details.append("rebase in progress")
892 if not get_all:
893 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700895 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
896 if changes:
897 details.extend(changes)
898 if not get_all:
899 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500900
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700901 changes = self.work_git.DiffZ('diff-files').keys()
902 if changes:
903 details.extend(changes)
904 if not get_all:
905 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500906
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700907 changes = self.work_git.LsOthers()
908 if changes:
909 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500910
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700911 return details
912
913 def HasChanges(self):
914 """Returns true if there are uncommitted changes.
915 """
916 if self.UncommitedFiles(get_all=False):
917 return True
918 else:
919 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500920
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600921 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200923
924 Args:
925 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600926 quiet: If True then only print the project name. Do not print
927 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 """
929 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700930 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200931 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700932 print(file=output_redir)
933 print('project %s/' % self.relpath, file=output_redir)
934 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 return
936
937 self.work_git.update_index('-q',
938 '--unmerged',
939 '--ignore-missing',
940 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700941 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
943 df = self.work_git.DiffZ('diff-files')
944 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100945 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700946 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947
948 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700949 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200950 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700951 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600953 if quiet:
954 out.nl()
955 return 'DIRTY'
956
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957 branch = self.CurrentBranch
958 if branch is None:
959 out.nobranch('(*** NO BRANCH ***)')
960 else:
961 out.branch('branch %s', branch)
962 out.nl()
963
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700964 if rb:
965 out.important('prior sync failed; rebase still in progress')
966 out.nl()
967
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 paths = list()
969 paths.extend(di.keys())
970 paths.extend(df.keys())
971 paths.extend(do)
972
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530973 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900974 try:
975 i = di[p]
976 except KeyError:
977 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900979 try:
980 f = df[p]
981 except KeyError:
982 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200983
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 if i:
985 i_status = i.status.upper()
986 else:
987 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 if f:
990 f_status = f.status.lower()
991 else:
992 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
994 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800995 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700996 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997 else:
998 line = ' %s%s\t%s' % (i_status, f_status, p)
999
1000 if i and not f:
1001 out.added('%s', line)
1002 elif (i and f) or (not i and f):
1003 out.changed('%s', line)
1004 elif not i and not f:
1005 out.untracked('%s', line)
1006 else:
1007 out.write('%s', line)
1008 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001009
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001010 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001011
pelyad67872d2012-03-28 14:49:58 +03001012 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001013 """Prints the status of the repository to stdout.
1014 """
1015 out = DiffColoring(self.config)
1016 cmd = ['diff']
1017 if out.is_on:
1018 cmd.append('--color')
1019 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001020 if absolute_paths:
1021 cmd.append('--src-prefix=a/%s/' % self.relpath)
1022 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023 cmd.append('--')
1024 p = GitCommand(self,
1025 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001026 capture_stdout=True,
1027 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028 has_diff = False
1029 for line in p.process.stdout:
1030 if not has_diff:
1031 out.nl()
1032 out.project('project %s/' % self.relpath)
1033 out.nl()
1034 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001035 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 p.Wait()
1037
1038
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001039# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040
David Pursehouse8a68ff92012-09-24 12:15:13 +09001041 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 """Was the branch published (uploaded) for code review?
1043 If so, returns the SHA-1 hash of the last published
1044 state for the branch.
1045 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001046 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001047 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001048 try:
1049 return self.bare_git.rev_parse(key)
1050 except GitError:
1051 return None
1052 else:
1053 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 except KeyError:
1056 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057
David Pursehouse8a68ff92012-09-24 12:15:13 +09001058 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059 """Prunes any stale published refs.
1060 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001061 if all_refs is None:
1062 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 heads = set()
1064 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301065 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 if name.startswith(R_HEADS):
1067 heads.add(name)
1068 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301071 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 n = name[len(R_PUB):]
1073 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001074 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001076 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 """List any branches which can be uploaded for review.
1078 """
1079 heads = {}
1080 pubed = {}
1081
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301082 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001084 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001086 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
1088 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001090 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001092 if selected_branch and branch != selected_branch:
1093 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001095 rb = self.GetUploadableBranch(branch)
1096 if rb:
1097 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 return ready
1099
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001100 def GetUploadableBranch(self, branch_name):
1101 """Get a single uploadable branch, or None.
1102 """
1103 branch = self.GetBranch(branch_name)
1104 base = branch.LocalMerge
1105 if branch.LocalMerge:
1106 rb = ReviewableBranch(self, branch, base)
1107 if rb.commits:
1108 return rb
1109 return None
1110
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001111 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001112 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001113 auto_topic=False,
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001114 draft=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001115 private=False,
1116 wip=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001117 dest_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001118 """Uploads the named branch for code review.
1119 """
1120 if branch is None:
1121 branch = self.CurrentBranch
1122 if branch is None:
1123 raise GitError('not currently on a branch')
1124
1125 branch = self.GetBranch(branch)
1126 if not branch.LocalMerge:
1127 raise GitError('branch %s does not track a remote' % branch.name)
1128 if not branch.remote.review:
1129 raise GitError('remote %s has no review url' % branch.remote.name)
1130
Bryan Jacobsf609f912013-05-06 13:36:24 -04001131 if dest_branch is None:
1132 dest_branch = self.dest_branch
1133 if dest_branch is None:
1134 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001135 if not dest_branch.startswith(R_HEADS):
1136 dest_branch = R_HEADS + dest_branch
1137
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001138 if not branch.remote.projectname:
1139 branch.remote.projectname = self.name
1140 branch.remote.Save()
1141
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001142 url = branch.remote.ReviewUrl(self.UserEmail)
1143 if url is None:
1144 raise UploadError('review not configured')
1145 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001146
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001147 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001148 rp = ['gerrit receive-pack']
1149 for e in people[0]:
1150 rp.append('--reviewer=%s' % sq(e))
1151 for e in people[1]:
1152 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001153 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001154
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001155 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001156
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001157 if dest_branch.startswith(R_HEADS):
1158 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001159
Jonathan Niederc94d6eb2017-08-08 18:34:53 +00001160 upload_type = 'for'
1161 if draft:
1162 upload_type = 'drafts'
1163
1164 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1165 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001166 if auto_topic:
1167 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001168
Shawn Pearce45d21682013-02-28 00:35:51 -08001169 if not url.startswith('ssh://'):
1170 rp = ['r=%s' % p for p in people[0]] + \
1171 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001172 if private:
1173 rp = rp + ['private']
1174 if wip:
1175 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001176 if rp:
1177 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001178 cmd.append(ref_spec)
1179
Anthony King7bdac712014-07-16 12:56:40 +01001180 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001181 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001182
1183 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1184 self.bare_git.UpdateRef(R_PUB + branch.name,
1185 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001186 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
1188
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001189# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001190
Julien Campergue335f5ef2013-10-16 11:02:35 +02001191 def _ExtractArchive(self, tarpath, path=None):
1192 """Extract the given tar on its current location
1193
1194 Args:
1195 - tarpath: The path to the actual tar file
1196
1197 """
1198 try:
1199 with tarfile.open(tarpath, 'r') as tar:
1200 tar.extractall(path=path)
1201 return True
1202 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001203 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001204 return False
1205
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001206 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001207 quiet=False,
1208 is_new=None,
1209 current_branch_only=False,
1210 force_sync=False,
1211 clone_bundle=True,
1212 no_tags=False,
1213 archive=False,
1214 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001215 prune=False,
1216 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001217 """Perform only the network IO portion of the sync process.
1218 Local working directory/branch state is not affected.
1219 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001220 if archive and not isinstance(self, MetaProject):
1221 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001222 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001223 return False
1224
1225 name = self.relpath.replace('\\', '/')
1226 name = name.replace('/', '_')
1227 tarpath = '%s.tar' % name
1228 topdir = self.manifest.topdir
1229
1230 try:
1231 self._FetchArchive(tarpath, cwd=topdir)
1232 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001233 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001234 return False
1235
1236 # From now on, we only need absolute tarpath
1237 tarpath = os.path.join(topdir, tarpath)
1238
1239 if not self._ExtractArchive(tarpath, path=topdir):
1240 return False
1241 try:
1242 os.remove(tarpath)
1243 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001244 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001245 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001246 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001247 if is_new is None:
1248 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001249 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001250 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001251 else:
1252 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001253 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001254
1255 if is_new:
1256 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1257 try:
1258 fd = open(alt, 'rb')
1259 try:
1260 alt_dir = fd.readline().rstrip()
1261 finally:
1262 fd.close()
1263 except IOError:
1264 alt_dir = None
1265 else:
1266 alt_dir = None
1267
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001268 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001269 and alt_dir is None \
1270 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001271 is_new = False
1272
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001273 if not current_branch_only:
1274 if self.sync_c:
1275 current_branch_only = True
1276 elif not self.manifest._loaded:
1277 # Manifest cannot check defaults until it syncs.
1278 current_branch_only = False
1279 elif self.manifest.default.sync_c:
1280 current_branch_only = True
1281
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001282 if self.clone_depth:
1283 depth = self.clone_depth
1284 else:
1285 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1286
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001287 need_to_fetch = not (optimized_fetch and
1288 (ID_RE.match(self.revisionExpr) and
Zac Livingstone4332262017-06-16 08:56:09 -06001289 self._CheckForImmutableRevision()))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001290 if (need_to_fetch and
1291 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1292 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001293 no_tags=no_tags, prune=prune, depth=depth,
1294 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001295 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001296
1297 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001298 self._InitMRef()
1299 else:
1300 self._InitMirrorHead()
1301 try:
1302 os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
1303 except OSError:
1304 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001305 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001306
1307 def PostRepoUpgrade(self):
1308 self._InitHooks()
1309
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001310 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001311 if self.manifest.isGitcClient:
1312 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001313 for copyfile in self.copyfiles:
1314 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001315 for linkfile in self.linkfiles:
1316 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001317
Julien Camperguedd654222014-01-09 16:21:37 +01001318 def GetCommitRevisionId(self):
1319 """Get revisionId of a commit.
1320
1321 Use this method instead of GetRevisionId to get the id of the commit rather
1322 than the id of the current git object (for example, a tag)
1323
1324 """
1325 if not self.revisionExpr.startswith(R_TAGS):
1326 return self.GetRevisionId(self._allrefs)
1327
1328 try:
1329 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1330 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001331 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1332 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001333
David Pursehouse8a68ff92012-09-24 12:15:13 +09001334 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001335 if self.revisionId:
1336 return self.revisionId
1337
1338 rem = self.GetRemote(self.remote.name)
1339 rev = rem.ToLocal(self.revisionExpr)
1340
David Pursehouse8a68ff92012-09-24 12:15:13 +09001341 if all_refs is not None and rev in all_refs:
1342 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001343
1344 try:
1345 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1346 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001347 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1348 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001349
Martin Kellye4e94d22017-03-21 16:05:12 -07001350 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001351 """Perform only the local IO portion of the sync process.
1352 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001353 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001354 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001355 all_refs = self.bare_ref.all
1356 self.CleanPublishedCache(all_refs)
1357 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001358
David Pursehouse1d947b32012-10-25 12:23:11 +09001359 def _doff():
1360 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001361 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001362
Martin Kellye4e94d22017-03-21 16:05:12 -07001363 def _dosubmodules():
1364 self._SyncSubmodules(quiet=True)
1365
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001366 head = self.work_git.GetHead()
1367 if head.startswith(R_HEADS):
1368 branch = head[len(R_HEADS):]
1369 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001370 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001371 except KeyError:
1372 head = None
1373 else:
1374 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001375
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001376 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001377 # Currently on a detached HEAD. The user is assumed to
1378 # not have any local modifications worth worrying about.
1379 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001380 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001381 syncbuf.fail(self, _PriorSyncFailedError())
1382 return
1383
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001384 if head == revid:
1385 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001386 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001387 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001388 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001389 # The copy/linkfile config may have changed.
1390 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001391 return
1392 else:
1393 lost = self._revlist(not_rev(revid), HEAD)
1394 if lost:
1395 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001396
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001397 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001398 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001399 if submodules:
1400 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001401 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001402 syncbuf.fail(self, e)
1403 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001404 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001405 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001406
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001407 if head == revid:
1408 # No changes; don't do anything further.
1409 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001410 # The copy/linkfile config may have changed.
1411 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001412 return
1413
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001414 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001415
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001416 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001417 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001418 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001419 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001420 syncbuf.info(self,
1421 "leaving %s; does not track upstream",
1422 branch.name)
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. Pearce3c8dea12009-05-29 18:38:17 -07001433 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001434 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001435 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001436 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001437 if not_merged:
1438 if upstream_gain:
1439 # The user has published this branch and some of those
1440 # commits are not yet merged upstream. We do not want
1441 # to rewrite the published commits so we punt.
1442 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001443 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001444 "branch %s is published (but not merged) and is now "
1445 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001446 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001447 elif pub == head:
1448 # All published commits are merged, and thus we are a
1449 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001450 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001451 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001452 if submodules:
1453 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001454 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001455
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001456 # Examine the local commits not in the remote. Find the
1457 # last one attributed to this user, if any.
1458 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001459 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001460 last_mine = None
1461 cnt_mine = 0
1462 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301463 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001464 if committer_email == self.UserEmail:
1465 last_mine = commit_id
1466 cnt_mine += 1
1467
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001468 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001469 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001470
1471 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001472 syncbuf.fail(self, _DirtyError())
1473 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001474
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001475 # If the upstream switched on us, warn the user.
1476 #
1477 if branch.merge != self.revisionExpr:
1478 if branch.merge and self.revisionExpr:
1479 syncbuf.info(self,
1480 'manifest switched %s...%s',
1481 branch.merge,
1482 self.revisionExpr)
1483 elif branch.merge:
1484 syncbuf.info(self,
1485 'manifest no longer tracks %s',
1486 branch.merge)
1487
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001488 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001489 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001490 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001491 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001492 syncbuf.info(self,
1493 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001494 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001495
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001496 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001497 if not ID_RE.match(self.revisionExpr):
1498 # in case of manifest sync the revisionExpr might be a SHA1
1499 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001500 if not branch.merge.startswith('refs/'):
1501 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001502 branch.Save()
1503
Mike Pontillod3153822012-02-28 11:53:24 -08001504 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001505 def _docopyandlink():
1506 self._CopyAndLinkFiles()
1507
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001508 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001509 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001510 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001511 if submodules:
1512 syncbuf.later2(self, _dosubmodules)
1513 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001514 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001516 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001517 if submodules:
1518 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001519 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001520 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001521 syncbuf.fail(self, e)
1522 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001523 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001524 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001525 if submodules:
1526 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001527
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001528 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001529 # dest should already be an absolute path, but src is project relative
1530 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001531 abssrc = os.path.join(self.worktree, src)
1532 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001533
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001534 def AddLinkFile(self, src, dest, absdest):
1535 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001536 # make src relative path to dest
1537 absdestdir = os.path.dirname(absdest)
1538 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001539 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001540
James W. Mills24c13082012-04-12 15:04:13 -05001541 def AddAnnotation(self, name, value, keep):
1542 self.annotations.append(_Annotation(name, value, keep))
1543
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001544 def DownloadPatchSet(self, change_id, patch_id):
1545 """Download a single patch set of a single change to FETCH_HEAD.
1546 """
1547 remote = self.GetRemote(self.remote.name)
1548
1549 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001550 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001551 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001552 if GitCommand(self, cmd, bare=True).Wait() != 0:
1553 return None
1554 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001555 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001556 change_id,
1557 patch_id,
1558 self.bare_git.rev_parse('FETCH_HEAD'))
1559
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001560
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001561# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562
Simran Basib9a1b732015-08-20 12:19:28 -07001563 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001564 """Create a new branch off the manifest's revision.
1565 """
Simran Basib9a1b732015-08-20 12:19:28 -07001566 if not branch_merge:
1567 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001568 head = self.work_git.GetHead()
1569 if head == (R_HEADS + name):
1570 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001571
David Pursehouse8a68ff92012-09-24 12:15:13 +09001572 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001573 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001574 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001575 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001576 capture_stdout=True,
1577 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001578
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001579 branch = self.GetBranch(name)
1580 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001581 branch.merge = branch_merge
1582 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1583 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001584 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001585
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001586 if head.startswith(R_HEADS):
1587 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001588 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001589 except KeyError:
1590 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001591 if revid and head and revid == head:
1592 ref = os.path.join(self.gitdir, R_HEADS + name)
1593 try:
1594 os.makedirs(os.path.dirname(ref))
1595 except OSError:
1596 pass
1597 _lwrite(ref, '%s\n' % revid)
1598 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1599 'ref: %s%s\n' % (R_HEADS, name))
1600 branch.Save()
1601 return True
1602
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001603 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001604 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001605 capture_stdout=True,
1606 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001607 branch.Save()
1608 return True
1609 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001610
Wink Saville02d79452009-04-10 13:01:24 -07001611 def CheckoutBranch(self, name):
1612 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001613
1614 Args:
1615 name: The name of the branch to checkout.
1616
1617 Returns:
1618 True if the checkout succeeded; False if it didn't; None if the branch
1619 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001620 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001621 rev = R_HEADS + name
1622 head = self.work_git.GetHead()
1623 if head == rev:
1624 # Already on the branch
1625 #
1626 return True
Wink Saville02d79452009-04-10 13:01:24 -07001627
David Pursehouse8a68ff92012-09-24 12:15:13 +09001628 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001629 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001630 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001631 except KeyError:
1632 # Branch does not exist in this project
1633 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001634 return None
Wink Saville02d79452009-04-10 13:01:24 -07001635
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001636 if head.startswith(R_HEADS):
1637 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001638 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001639 except KeyError:
1640 head = None
1641
1642 if head == revid:
1643 # Same revision; just update HEAD to point to the new
1644 # target branch, but otherwise take no other action.
1645 #
1646 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1647 'ref: %s%s\n' % (R_HEADS, name))
1648 return True
1649
1650 return GitCommand(self,
1651 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001652 capture_stdout=True,
1653 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001654
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001655 def AbandonBranch(self, name):
1656 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001657
1658 Args:
1659 name: The name of the branch to abandon.
1660
1661 Returns:
1662 True if the abandon succeeded; False if it didn't; None if the branch
1663 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001664 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001665 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001666 all_refs = self.bare_ref.all
1667 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001668 # Doesn't exist
1669 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001670
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001671 head = self.work_git.GetHead()
1672 if head == rev:
1673 # We can't destroy the branch while we are sitting
1674 # on it. Switch to a detached HEAD.
1675 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001676 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001677
David Pursehouse8a68ff92012-09-24 12:15:13 +09001678 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001679 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001680 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1681 '%s\n' % revid)
1682 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001683 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001684
1685 return GitCommand(self,
1686 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001687 capture_stdout=True,
1688 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001689
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001690 def PruneHeads(self):
1691 """Prune any topic branches already merged into upstream.
1692 """
1693 cb = self.CurrentBranch
1694 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001695 left = self._allrefs
1696 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001697 if name.startswith(R_HEADS):
1698 name = name[len(R_HEADS):]
1699 if cb is None or name != cb:
1700 kill.append(name)
1701
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001702 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001703 if cb is not None \
1704 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001705 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001706 self.work_git.DetachHead(HEAD)
1707 kill.append(cb)
1708
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001709 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001710 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001711
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001712 try:
1713 self.bare_git.DetachHead(rev)
1714
1715 b = ['branch', '-d']
1716 b.extend(kill)
1717 b = GitCommand(self, b, bare=True,
1718 capture_stdout=True,
1719 capture_stderr=True)
1720 b.Wait()
1721 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001722 if ID_RE.match(old):
1723 self.bare_git.DetachHead(old)
1724 else:
1725 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001726 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001728 for branch in kill:
1729 if (R_HEADS + branch) not in left:
1730 self.CleanPublishedCache()
1731 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001732
1733 if cb and cb not in kill:
1734 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001735 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001736
1737 kept = []
1738 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001739 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001740 branch = self.GetBranch(branch)
1741 base = branch.LocalMerge
1742 if not base:
1743 base = rev
1744 kept.append(ReviewableBranch(self, branch, base))
1745 return kept
1746
1747
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001748# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001749
1750 def GetRegisteredSubprojects(self):
1751 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001752
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001753 def rec(subprojects):
1754 if not subprojects:
1755 return
1756 result.extend(subprojects)
1757 for p in subprojects:
1758 rec(p.subprojects)
1759 rec(self.subprojects)
1760 return result
1761
1762 def _GetSubmodules(self):
1763 # Unfortunately we cannot call `git submodule status --recursive` here
1764 # because the working tree might not exist yet, and it cannot be used
1765 # without a working tree in its current implementation.
1766
1767 def get_submodules(gitdir, rev):
1768 # Parse .gitmodules for submodule sub_paths and sub_urls
1769 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1770 if not sub_paths:
1771 return []
1772 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1773 # revision of submodule repository
1774 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1775 submodules = []
1776 for sub_path, sub_url in zip(sub_paths, sub_urls):
1777 try:
1778 sub_rev = sub_revs[sub_path]
1779 except KeyError:
1780 # Ignore non-exist submodules
1781 continue
1782 submodules.append((sub_rev, sub_path, sub_url))
1783 return submodules
1784
1785 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1786 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001787
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001788 def parse_gitmodules(gitdir, rev):
1789 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1790 try:
Anthony King7bdac712014-07-16 12:56:40 +01001791 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1792 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001793 except GitError:
1794 return [], []
1795 if p.Wait() != 0:
1796 return [], []
1797
1798 gitmodules_lines = []
1799 fd, temp_gitmodules_path = tempfile.mkstemp()
1800 try:
1801 os.write(fd, p.stdout)
1802 os.close(fd)
1803 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001804 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1805 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001806 if p.Wait() != 0:
1807 return [], []
1808 gitmodules_lines = p.stdout.split('\n')
1809 except GitError:
1810 return [], []
1811 finally:
1812 os.remove(temp_gitmodules_path)
1813
1814 names = set()
1815 paths = {}
1816 urls = {}
1817 for line in gitmodules_lines:
1818 if not line:
1819 continue
1820 m = re_path.match(line)
1821 if m:
1822 names.add(m.group(1))
1823 paths[m.group(1)] = m.group(2)
1824 continue
1825 m = re_url.match(line)
1826 if m:
1827 names.add(m.group(1))
1828 urls[m.group(1)] = m.group(2)
1829 continue
1830 names = sorted(names)
1831 return ([paths.get(name, '') for name in names],
1832 [urls.get(name, '') for name in names])
1833
1834 def git_ls_tree(gitdir, rev, paths):
1835 cmd = ['ls-tree', rev, '--']
1836 cmd.extend(paths)
1837 try:
Anthony King7bdac712014-07-16 12:56:40 +01001838 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1839 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001840 except GitError:
1841 return []
1842 if p.Wait() != 0:
1843 return []
1844 objects = {}
1845 for line in p.stdout.split('\n'):
1846 if not line.strip():
1847 continue
1848 object_rev, object_path = line.split()[2:4]
1849 objects[object_path] = object_rev
1850 return objects
1851
1852 try:
1853 rev = self.GetRevisionId()
1854 except GitError:
1855 return []
1856 return get_submodules(self.gitdir, rev)
1857
1858 def GetDerivedSubprojects(self):
1859 result = []
1860 if not self.Exists:
1861 # If git repo does not exist yet, querying its submodules will
1862 # mess up its states; so return here.
1863 return result
1864 for rev, path, url in self._GetSubmodules():
1865 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001866 relpath, worktree, gitdir, objdir = \
1867 self.manifest.GetSubprojectPaths(self, name, path)
1868 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001869 if project:
1870 result.extend(project.GetDerivedSubprojects())
1871 continue
David James8d201162013-10-11 17:03:19 -07001872
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001873 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001874 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001875 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001876 review=self.remote.review,
1877 revision=self.remote.revision)
1878 subproject = Project(manifest=self.manifest,
1879 name=name,
1880 remote=remote,
1881 gitdir=gitdir,
1882 objdir=objdir,
1883 worktree=worktree,
1884 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001885 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001886 revisionId=rev,
1887 rebase=self.rebase,
1888 groups=self.groups,
1889 sync_c=self.sync_c,
1890 sync_s=self.sync_s,
1891 parent=self,
1892 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001893 result.append(subproject)
1894 result.extend(subproject.GetDerivedSubprojects())
1895 return result
1896
1897
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001898# Direct Git Commands ##
Zac Livingstone4332262017-06-16 08:56:09 -06001899 def _CheckForImmutableRevision(self):
Chris AtLee2fb64662014-01-16 21:32:33 -05001900 try:
1901 # if revision (sha or tag) is not present then following function
1902 # throws an error.
1903 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1904 return True
1905 except GitError:
1906 # There is no such persistent revision. We have to fetch it.
1907 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001908
Julien Campergue335f5ef2013-10-16 11:02:35 +02001909 def _FetchArchive(self, tarpath, cwd=None):
1910 cmd = ['archive', '-v', '-o', tarpath]
1911 cmd.append('--remote=%s' % self.remote.url)
1912 cmd.append('--prefix=%s/' % self.relpath)
1913 cmd.append(self.revisionExpr)
1914
1915 command = GitCommand(self, cmd, cwd=cwd,
1916 capture_stdout=True,
1917 capture_stderr=True)
1918
1919 if command.Wait() != 0:
1920 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1921
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001922 def _RemoteFetch(self, name=None,
1923 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001924 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001925 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001926 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001927 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001928 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001929 depth=None,
1930 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001931
1932 is_sha1 = False
1933 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001934 # The depth should not be used when fetching to a mirror because
1935 # it will result in a shallow repository that cannot be cloned or
1936 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001937 # The repo project should also never be synced with partial depth.
1938 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1939 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001940
Shawn Pearce69e04d82014-01-29 12:48:54 -08001941 if depth:
1942 current_branch_only = True
1943
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001944 if ID_RE.match(self.revisionExpr) is not None:
1945 is_sha1 = True
1946
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001947 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001948 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001949 # this is a tag and its sha1 value should never change
1950 tag_name = self.revisionExpr[len(R_TAGS):]
1951
1952 if is_sha1 or tag_name is not None:
Zac Livingstone4332262017-06-16 08:56:09 -06001953 if self._CheckForImmutableRevision():
1954 print('Skipped fetching project %s (already have persistent ref)'
1955 % self.name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001956 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001957 if is_sha1 and not depth:
1958 # When syncing a specific commit and --depth is not set:
1959 # * if upstream is explicitly specified and is not a sha1, fetch only
1960 # upstream as users expect only upstream to be fetch.
1961 # Note: The commit might not be in upstream in which case the sync
1962 # will fail.
1963 # * otherwise, fetch all branches to make sure we end up with the
1964 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001965 if self.upstream:
1966 current_branch_only = not ID_RE.match(self.upstream)
1967 else:
1968 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001969
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001970 if not name:
1971 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001972
1973 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001974 remote = self.GetRemote(name)
1975 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001976 ssh_proxy = True
1977
Shawn O. Pearce88443382010-10-08 10:02:09 +02001978 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001979 if alt_dir and 'objects' == os.path.basename(alt_dir):
1980 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001981 packed_refs = os.path.join(self.gitdir, 'packed-refs')
1982 remote = self.GetRemote(name)
1983
David Pursehouse8a68ff92012-09-24 12:15:13 +09001984 all_refs = self.bare_ref.all
1985 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02001986 tmp = set()
1987
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301988 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001989 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001990 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001991 all_refs[r] = ref_id
1992 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001993 continue
1994
David Pursehouse8a68ff92012-09-24 12:15:13 +09001995 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001996 continue
1997
David Pursehouse8a68ff92012-09-24 12:15:13 +09001998 r = 'refs/_alt/%s' % ref_id
1999 all_refs[r] = ref_id
2000 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002001 tmp.add(r)
2002
heping3d7bbc92017-04-12 19:51:47 +08002003 tmp_packed_lines = []
2004 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02002005
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302006 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002007 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08002008 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002009 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08002010 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002011
heping3d7bbc92017-04-12 19:51:47 +08002012 tmp_packed = ''.join(tmp_packed_lines)
2013 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002014 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002015 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002016 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002017
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002018 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002019
Conley Owensf97e8382015-01-21 11:12:46 -08002020 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002021 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002022 else:
2023 # If this repo has shallow objects, then we don't know which refs have
2024 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2025 # do this with projects that don't have shallow objects, since it is less
2026 # efficient.
2027 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2028 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002029
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002030 if quiet:
2031 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002032 if not self.worktree:
2033 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002034 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002035
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002036 # If using depth then we should not get all the tags since they may
2037 # be outside of the depth.
2038 if no_tags or depth:
2039 cmd.append('--no-tags')
2040 else:
2041 cmd.append('--tags')
2042
David Pursehouse74cfd272015-10-14 10:50:15 +09002043 if prune:
2044 cmd.append('--prune')
2045
Martin Kellye4e94d22017-03-21 16:05:12 -07002046 if submodules:
2047 cmd.append('--recurse-submodules=on-demand')
2048
Conley Owens80b87fe2014-05-09 17:13:44 -07002049 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002050 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002051 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002052 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002053 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002054 spec.append('tag')
2055 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002056
David Pursehouse403b64e2015-04-27 10:41:33 +09002057 if not self.manifest.IsMirror:
2058 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002059 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002060 # Shallow checkout of a specific commit, fetch from that commit and not
2061 # the heads only as the commit might be deeper in the history.
2062 spec.append(branch)
2063 else:
2064 if is_sha1:
2065 branch = self.upstream
2066 if branch is not None and branch.strip():
2067 if not branch.startswith('refs/'):
2068 branch = R_HEADS + branch
2069 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002070 cmd.extend(spec)
2071
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002072 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002073 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002074 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002075 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002076 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002077 ok = True
2078 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002079 # If needed, run the 'git remote prune' the first time through the loop
2080 elif (not _i and
2081 "error:" in gitcmd.stderr and
2082 "git remote prune" in gitcmd.stderr):
2083 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002084 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002085 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002086 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002087 break
2088 continue
Brian Harring14a66742012-09-28 20:21:57 -07002089 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002090 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2091 # in sha1 mode, we just tried sync'ing from the upstream field; it
2092 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002093 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002094 elif ret < 0:
2095 # Git died with a signal, exit immediately
2096 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002097 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002098
2099 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002100 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002101 if old_packed != '':
2102 _lwrite(packed_refs, old_packed)
2103 else:
2104 os.remove(packed_refs)
2105 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002106
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002107 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002108 # We just synced the upstream given branch; verify we
2109 # got what we wanted, else trigger a second run of all
2110 # refs.
Zac Livingstone4332262017-06-16 08:56:09 -06002111 if not self._CheckForImmutableRevision():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002112 if current_branch_only and depth:
2113 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002114 return self._RemoteFetch(name=name,
2115 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002116 initial=False, quiet=quiet, alt_dir=alt_dir,
2117 depth=None)
2118 else:
2119 # Avoid infinite recursion: sync all branches with depth set to None
2120 return self._RemoteFetch(name=name, current_branch_only=False,
2121 initial=False, quiet=quiet, alt_dir=alt_dir,
2122 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002123
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002124 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002125
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002126 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002127 if initial and \
2128 (self.manifest.manifestProject.config.GetString('repo.depth') or
2129 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002130 return False
2131
2132 remote = self.GetRemote(self.remote.name)
2133 bundle_url = remote.url + '/clone.bundle'
2134 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002135 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2136 'persistent-http',
2137 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002138 return False
2139
2140 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2141 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2142
2143 exist_dst = os.path.exists(bundle_dst)
2144 exist_tmp = os.path.exists(bundle_tmp)
2145
2146 if not initial and not exist_dst and not exist_tmp:
2147 return False
2148
2149 if not exist_dst:
2150 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2151 if not exist_dst:
2152 return False
2153
2154 cmd = ['fetch']
2155 if quiet:
2156 cmd.append('--quiet')
2157 if not self.worktree:
2158 cmd.append('--update-head-ok')
2159 cmd.append(bundle_dst)
2160 for f in remote.fetch:
2161 cmd.append(str(f))
2162 cmd.append('refs/tags/*:refs/tags/*')
2163
2164 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002165 if os.path.exists(bundle_dst):
2166 os.remove(bundle_dst)
2167 if os.path.exists(bundle_tmp):
2168 os.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002169 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002170
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002171 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002172 if os.path.exists(dstPath):
2173 os.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002174
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002175 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002176 if quiet:
2177 cmd += ['--silent']
2178 if os.path.exists(tmpPath):
2179 size = os.stat(tmpPath).st_size
2180 if size >= 1024:
2181 cmd += ['--continue-at', '%d' % (size,)]
2182 else:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002183 os.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002184 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2185 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002186 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002187 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002188 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002189 if srcUrl.startswith('persistent-'):
2190 srcUrl = srcUrl[len('persistent-'):]
2191 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002192
Dave Borowitz137d0132015-01-02 11:12:54 -08002193 if IsTrace():
2194 Trace('%s', ' '.join(cmd))
2195 try:
2196 proc = subprocess.Popen(cmd)
2197 except OSError:
2198 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002199
Dave Borowitz137d0132015-01-02 11:12:54 -08002200 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002201
Dave Borowitz137d0132015-01-02 11:12:54 -08002202 if curlret == 22:
2203 # From curl man page:
2204 # 22: HTTP page not retrieved. The requested url was not found or
2205 # returned another error with the HTTP error code being 400 or above.
2206 # This return code only appears if -f, --fail is used.
2207 if not quiet:
2208 print("Server does not provide clone.bundle; ignoring.",
2209 file=sys.stderr)
2210 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002211
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002212 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002213 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Renaud Paquayad1abcb2016-11-01 11:34:55 -07002214 platform_utils.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002215 return True
2216 else:
2217 os.remove(tmpPath)
2218 return False
2219 else:
2220 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002221
Kris Giesingc8d882a2014-12-23 13:02:32 -08002222 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002223 try:
2224 with open(path) as f:
2225 if f.read(16) == '# v2 git bundle\n':
2226 return True
2227 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002228 if not quiet:
2229 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002230 return False
2231 except OSError:
2232 return False
2233
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002234 def _Checkout(self, rev, quiet=False):
2235 cmd = ['checkout']
2236 if quiet:
2237 cmd.append('-q')
2238 cmd.append(rev)
2239 cmd.append('--')
2240 if GitCommand(self, cmd).Wait() != 0:
2241 if self._allrefs:
2242 raise GitError('%s checkout %s ' % (self.name, rev))
2243
Anthony King7bdac712014-07-16 12:56:40 +01002244 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002245 cmd = ['cherry-pick']
2246 cmd.append(rev)
2247 cmd.append('--')
2248 if GitCommand(self, cmd).Wait() != 0:
2249 if self._allrefs:
2250 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2251
Anthony King7bdac712014-07-16 12:56:40 +01002252 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002253 cmd = ['revert']
2254 cmd.append('--no-edit')
2255 cmd.append(rev)
2256 cmd.append('--')
2257 if GitCommand(self, cmd).Wait() != 0:
2258 if self._allrefs:
2259 raise GitError('%s revert %s ' % (self.name, rev))
2260
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002261 def _ResetHard(self, rev, quiet=True):
2262 cmd = ['reset', '--hard']
2263 if quiet:
2264 cmd.append('-q')
2265 cmd.append(rev)
2266 if GitCommand(self, cmd).Wait() != 0:
2267 raise GitError('%s reset --hard %s ' % (self.name, rev))
2268
Martin Kellye4e94d22017-03-21 16:05:12 -07002269 def _SyncSubmodules(self, quiet=True):
2270 cmd = ['submodule', 'update', '--init', '--recursive']
2271 if quiet:
2272 cmd.append('-q')
2273 if GitCommand(self, cmd).Wait() != 0:
2274 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2275
Anthony King7bdac712014-07-16 12:56:40 +01002276 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002277 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002278 if onto is not None:
2279 cmd.extend(['--onto', onto])
2280 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002281 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002282 raise GitError('%s rebase %s ' % (self.name, upstream))
2283
Pierre Tardy3d125942012-05-04 12:18:12 +02002284 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002285 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002286 if ffonly:
2287 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002288 if GitCommand(self, cmd).Wait() != 0:
2289 raise GitError('%s merge %s ' % (self.name, head))
2290
Kevin Degiabaa7f32014-11-12 11:27:45 -07002291 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002292 init_git_dir = not os.path.exists(self.gitdir)
2293 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002294 try:
2295 # Initialize the bare repository, which contains all of the objects.
2296 if init_obj_dir:
2297 os.makedirs(self.objdir)
2298 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002299
Kevin Degib1a07b82015-07-27 13:33:43 -06002300 # If we have a separate directory to hold refs, initialize it as well.
2301 if self.objdir != self.gitdir:
2302 if init_git_dir:
2303 os.makedirs(self.gitdir)
2304
2305 if init_obj_dir or init_git_dir:
2306 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2307 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002308 try:
2309 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2310 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002311 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002312 print("Retrying clone after deleting %s" %
2313 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002314 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002315 platform_utils.rmtree(os.path.realpath(self.gitdir))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002316 if self.worktree and os.path.exists(os.path.realpath
2317 (self.worktree)):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002318 platform_utils.rmtree(os.path.realpath(self.worktree))
Kevin Degiabaa7f32014-11-12 11:27:45 -07002319 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2320 except:
2321 raise e
2322 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002323
Kevin Degi384b3c52014-10-16 16:02:58 -06002324 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002325 mp = self.manifest.manifestProject
2326 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002327
Kevin Degib1a07b82015-07-27 13:33:43 -06002328 if ref_dir or mirror_git:
2329 if not mirror_git:
2330 mirror_git = os.path.join(ref_dir, self.name + '.git')
2331 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2332 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002333
Kevin Degib1a07b82015-07-27 13:33:43 -06002334 if os.path.exists(mirror_git):
2335 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002336
Kevin Degib1a07b82015-07-27 13:33:43 -06002337 elif os.path.exists(repo_git):
2338 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002339
Kevin Degib1a07b82015-07-27 13:33:43 -06002340 else:
2341 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002342
Kevin Degib1a07b82015-07-27 13:33:43 -06002343 if ref_dir:
2344 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2345 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002346
Kevin Degib1a07b82015-07-27 13:33:43 -06002347 self._UpdateHooks()
2348
2349 m = self.manifest.manifestProject.config
2350 for key in ['user.name', 'user.email']:
2351 if m.Has(key, include_defaults=False):
2352 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002353 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002354 if self.manifest.IsMirror:
2355 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002356 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002357 self.config.SetString('core.bare', None)
2358 except Exception:
2359 if init_obj_dir and os.path.exists(self.objdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002360 platform_utils.rmtree(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002361 if init_git_dir and os.path.exists(self.gitdir):
Renaud Paquaya65adf72016-11-03 10:37:53 -07002362 platform_utils.rmtree(self.gitdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002363 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002364
Jimmie Westera0444582012-10-24 13:44:42 +02002365 def _UpdateHooks(self):
2366 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002367 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002368
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002369 def _InitHooks(self):
Jesse Hall672cc492013-11-27 11:17:13 -08002370 hooks = os.path.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002371 if not os.path.exists(hooks):
2372 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002373 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002374 name = os.path.basename(stock_hook)
2375
Victor Boivie65e0f352011-04-18 11:23:29 +02002376 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002377 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002378 # Don't install a Gerrit Code Review hook if this
2379 # project does not appear to use it for reviews.
2380 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002381 # Since the manifest project is one of those, but also
2382 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002383 continue
2384
2385 dst = os.path.join(hooks, name)
2386 if os.path.islink(dst):
2387 continue
2388 if os.path.exists(dst):
2389 if filecmp.cmp(stock_hook, dst, shallow=False):
2390 os.remove(dst)
2391 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002392 _warn("%s: Not replacing locally modified %s hook",
2393 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002394 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002395 try:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002396 platform_utils.symlink(
2397 os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002398 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002399 if e.errno == errno.EPERM:
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002400 raise GitError('filesystem must support symlinks')
2401 else:
2402 raise
2403
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002404 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002405 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002406 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002407 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002408 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002409 remote.review = self.remote.review
2410 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002411
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002412 if self.worktree:
2413 remote.ResetFetch(mirror=False)
2414 else:
2415 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002416 remote.Save()
2417
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002418 def _InitMRef(self):
2419 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002420 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002421
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002422 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002423 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002424
2425 def _InitAnyMRef(self, ref):
2426 cur = self.bare_ref.symref(ref)
2427
2428 if self.revisionId:
2429 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2430 msg = 'manifest set to %s' % self.revisionId
2431 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002432 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002433 else:
2434 remote = self.GetRemote(self.remote.name)
2435 dst = remote.ToLocal(self.revisionExpr)
2436 if cur != dst:
2437 msg = 'manifest set to %s' % self.revisionExpr
2438 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002439
Kevin Degi384b3c52014-10-16 16:02:58 -06002440 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002441 symlink_files = self.shareable_files[:]
2442 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002443 if share_refs:
2444 symlink_files += self.working_tree_files
2445 symlink_dirs += self.working_tree_dirs
2446 to_symlink = symlink_files + symlink_dirs
2447 for name in set(to_symlink):
2448 dst = os.path.realpath(os.path.join(destdir, name))
2449 if os.path.lexists(dst):
2450 src = os.path.realpath(os.path.join(srcdir, name))
2451 # Fail if the links are pointing to the wrong place
2452 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002453 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002454 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002455 'work tree. If you\'re comfortable with the '
2456 'possibility of losing the work tree\'s git metadata,'
2457 ' use `repo sync --force-sync {0}` to '
2458 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002459
David James8d201162013-10-11 17:03:19 -07002460 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2461 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2462
2463 Args:
2464 gitdir: The bare git repository. Must already be initialized.
2465 dotgit: The repository you would like to initialize.
2466 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2467 Only one work tree can store refs under a given |gitdir|.
2468 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2469 This saves you the effort of initializing |dotgit| yourself.
2470 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002471 symlink_files = self.shareable_files[:]
2472 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002473 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002474 symlink_files += self.working_tree_files
2475 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002476 to_symlink = symlink_files + symlink_dirs
2477
2478 to_copy = []
2479 if copy_all:
2480 to_copy = os.listdir(gitdir)
2481
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002482 dotgit = os.path.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002483 for name in set(to_copy).union(to_symlink):
2484 try:
2485 src = os.path.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002486 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002487
Kevin Degi384b3c52014-10-16 16:02:58 -06002488 if os.path.lexists(dst):
2489 continue
David James8d201162013-10-11 17:03:19 -07002490
2491 # If the source dir doesn't exist, create an empty dir.
2492 if name in symlink_dirs and not os.path.lexists(src):
2493 os.makedirs(src)
2494
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002495 if name in to_symlink:
Renaud Paquayd5cec5e2016-11-01 11:24:03 -07002496 platform_utils.symlink(
2497 os.path.relpath(src, os.path.dirname(dst)), dst)
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002498 elif copy_all and not os.path.islink(dst):
2499 if os.path.isdir(src):
2500 shutil.copytree(src, dst)
2501 elif os.path.isfile(src):
2502 shutil.copy(src, dst)
2503
Conley Owens80b87fe2014-05-09 17:13:44 -07002504 # If the source file doesn't exist, ensure the destination
2505 # file doesn't either.
2506 if name in symlink_files and not os.path.lexists(src):
2507 try:
2508 os.remove(dst)
2509 except OSError:
2510 pass
2511
David James8d201162013-10-11 17:03:19 -07002512 except OSError as e:
2513 if e.errno == errno.EPERM:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002514 raise DownloadError('filesystem must support symlinks')
David James8d201162013-10-11 17:03:19 -07002515 else:
2516 raise
2517
Martin Kellye4e94d22017-03-21 16:05:12 -07002518 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002519 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002520 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002521 try:
2522 if init_dotgit:
2523 os.makedirs(dotgit)
2524 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2525 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002526
Kevin Degiabaa7f32014-11-12 11:27:45 -07002527 try:
2528 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2529 except GitError as e:
2530 if force_sync:
2531 try:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002532 platform_utils.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002533 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002534 except:
2535 raise e
2536 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002537
Kevin Degib1a07b82015-07-27 13:33:43 -06002538 if init_dotgit:
2539 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002540
Kevin Degib1a07b82015-07-27 13:33:43 -06002541 cmd = ['read-tree', '--reset', '-u']
2542 cmd.append('-v')
2543 cmd.append(HEAD)
2544 if GitCommand(self, cmd).Wait() != 0:
2545 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002546
Martin Kellye4e94d22017-03-21 16:05:12 -07002547 if submodules:
2548 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002549 self._CopyAndLinkFiles()
2550 except Exception:
2551 if init_dotgit:
Renaud Paquaya65adf72016-11-03 10:37:53 -07002552 platform_utils.rmtree(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002553 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002554
2555 def _gitdir_path(self, path):
David James8d201162013-10-11 17:03:19 -07002556 return os.path.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002557
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002558 def _revlist(self, *args, **kw):
2559 a = []
2560 a.extend(args)
2561 a.append('--')
2562 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002563
2564 @property
2565 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002566 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002567
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002568 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002569 """Get logs between two revisions of this project."""
2570 comp = '..'
2571 if rev1:
2572 revs = [rev1]
2573 if rev2:
2574 revs.extend([comp, rev2])
2575 cmd = ['log', ''.join(revs)]
2576 out = DiffColoring(self.config)
2577 if out.is_on and color:
2578 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002579 if pretty_format is not None:
2580 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002581 if oneline:
2582 cmd.append('--oneline')
2583
2584 try:
2585 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2586 if log.Wait() == 0:
2587 return log.stdout
2588 except GitError:
2589 # worktree may not exist if groups changed for example. In that case,
2590 # try in gitdir instead.
2591 if not os.path.exists(self.worktree):
2592 return self.bare_git.log(*cmd[1:])
2593 else:
2594 raise
2595 return None
2596
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002597 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2598 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002599 """Get the list of logs from this revision to given revisionId"""
2600 logs = {}
2601 selfId = self.GetRevisionId(self._allrefs)
2602 toId = toProject.GetRevisionId(toProject._allrefs)
2603
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002604 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2605 pretty_format=pretty_format)
2606 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2607 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002608 return logs
2609
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002610 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002611
David James8d201162013-10-11 17:03:19 -07002612 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002613 self._project = project
2614 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002615 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002616
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002617 def LsOthers(self):
2618 p = GitCommand(self._project,
2619 ['ls-files',
2620 '-z',
2621 '--others',
2622 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002623 bare=False,
David James8d201162013-10-11 17:03:19 -07002624 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002625 capture_stdout=True,
2626 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002627 if p.Wait() == 0:
2628 out = p.stdout
2629 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002630 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002631 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002632 return []
2633
2634 def DiffZ(self, name, *args):
2635 cmd = [name]
2636 cmd.append('-z')
2637 cmd.extend(args)
2638 p = GitCommand(self._project,
2639 cmd,
David James8d201162013-10-11 17:03:19 -07002640 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002641 bare=False,
2642 capture_stdout=True,
2643 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002644 try:
2645 out = p.process.stdout.read()
2646 r = {}
2647 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002648 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002649 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002650 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002651 info = next(out)
2652 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002653 except StopIteration:
2654 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002655
2656 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002657
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002658 def __init__(self, path, omode, nmode, oid, nid, state):
2659 self.path = path
2660 self.src_path = None
2661 self.old_mode = omode
2662 self.new_mode = nmode
2663 self.old_id = oid
2664 self.new_id = nid
2665
2666 if len(state) == 1:
2667 self.status = state
2668 self.level = None
2669 else:
2670 self.status = state[:1]
2671 self.level = state[1:]
2672 while self.level.startswith('0'):
2673 self.level = self.level[1:]
2674
2675 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002676 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002677 if info.status in ('R', 'C'):
2678 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002679 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002680 r[info.path] = info
2681 return r
2682 finally:
2683 p.Wait()
2684
2685 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002686 if self._bare:
2687 path = os.path.join(self._project.gitdir, HEAD)
2688 else:
2689 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002690 try:
2691 fd = open(path, 'rb')
Dan Sandler53e902a2014-03-09 13:20:02 -04002692 except IOError as e:
2693 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002694 try:
2695 line = fd.read()
2696 finally:
2697 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302698 try:
2699 line = line.decode()
2700 except AttributeError:
2701 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002702 if line.startswith('ref: '):
2703 return line[5:-1]
2704 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002705
2706 def SetHead(self, ref, message=None):
2707 cmdv = []
2708 if message is not None:
2709 cmdv.extend(['-m', message])
2710 cmdv.append(HEAD)
2711 cmdv.append(ref)
2712 self.symbolic_ref(*cmdv)
2713
2714 def DetachHead(self, new, message=None):
2715 cmdv = ['--no-deref']
2716 if message is not None:
2717 cmdv.extend(['-m', message])
2718 cmdv.append(HEAD)
2719 cmdv.append(new)
2720 self.update_ref(*cmdv)
2721
2722 def UpdateRef(self, name, new, old=None,
2723 message=None,
2724 detach=False):
2725 cmdv = []
2726 if message is not None:
2727 cmdv.extend(['-m', message])
2728 if detach:
2729 cmdv.append('--no-deref')
2730 cmdv.append(name)
2731 cmdv.append(new)
2732 if old is not None:
2733 cmdv.append(old)
2734 self.update_ref(*cmdv)
2735
2736 def DeleteRef(self, name, old=None):
2737 if not old:
2738 old = self.rev_parse(name)
2739 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002740 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002742 def rev_list(self, *args, **kw):
2743 if 'format' in kw:
2744 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2745 else:
2746 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002747 cmdv.extend(args)
2748 p = GitCommand(self._project,
2749 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002750 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002751 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002752 capture_stdout=True,
2753 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002754 r = []
2755 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002756 if line[-1] == '\n':
2757 line = line[:-1]
2758 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002759 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002760 raise GitError('%s rev-list %s: %s' %
2761 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002762 return r
2763
2764 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002765 """Allow arbitrary git commands using pythonic syntax.
2766
2767 This allows you to do things like:
2768 git_obj.rev_parse('HEAD')
2769
2770 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2771 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002772 Any other positional arguments will be passed to the git command, and the
2773 following keyword arguments are supported:
2774 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002775
2776 Args:
2777 name: The name of the git command to call. Any '_' characters will
2778 be replaced with '-'.
2779
2780 Returns:
2781 A callable object that will try to call git with the named command.
2782 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002783 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002784
Dave Borowitz091f8932012-10-23 17:01:04 -07002785 def runner(*args, **kwargs):
2786 cmdv = []
2787 config = kwargs.pop('config', None)
2788 for k in kwargs:
2789 raise TypeError('%s() got an unexpected keyword argument %r'
2790 % (name, k))
2791 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002792 if not git_require((1, 7, 2)):
2793 raise ValueError('cannot set config on command line for %s()'
2794 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302795 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002796 cmdv.append('-c')
2797 cmdv.append('%s=%s' % (k, v))
2798 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002799 cmdv.extend(args)
2800 p = GitCommand(self._project,
2801 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002802 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002803 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002804 capture_stdout=True,
2805 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002806 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002807 raise GitError('%s %s: %s' %
2808 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002809 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302810 try:
Conley Owensedd01512013-09-26 12:59:58 -07002811 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302812 except AttributeError:
2813 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002814 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2815 return r[:-1]
2816 return r
2817 return runner
2818
2819
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002820class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002821
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002822 def __str__(self):
2823 return 'prior sync failed; rebase still in progress'
2824
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002825
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002826class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002827
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002828 def __str__(self):
2829 return 'contains uncommitted changes'
2830
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002831
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002832class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002833
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002834 def __init__(self, project, text):
2835 self.project = project
2836 self.text = text
2837
2838 def Print(self, syncbuf):
2839 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2840 syncbuf.out.nl()
2841
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002842
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002843class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002844
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002845 def __init__(self, project, why):
2846 self.project = project
2847 self.why = why
2848
2849 def Print(self, syncbuf):
2850 syncbuf.out.fail('error: %s/: %s',
2851 self.project.relpath,
2852 str(self.why))
2853 syncbuf.out.nl()
2854
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002855
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002856class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002857
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002858 def __init__(self, project, action):
2859 self.project = project
2860 self.action = action
2861
2862 def Run(self, syncbuf):
2863 out = syncbuf.out
2864 out.project('project %s/', self.project.relpath)
2865 out.nl()
2866 try:
2867 self.action()
2868 out.nl()
2869 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002870 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871 out.nl()
2872 return False
2873
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002874
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002875class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002876
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002877 def __init__(self, config):
2878 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002879 self.project = self.printer('header', attr='bold')
2880 self.info = self.printer('info')
2881 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002882
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002883
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002884class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002885
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002886 def __init__(self, config, detach_head=False):
2887 self._messages = []
2888 self._failures = []
2889 self._later_queue1 = []
2890 self._later_queue2 = []
2891
2892 self.out = _SyncColoring(config)
2893 self.out.redirect(sys.stderr)
2894
2895 self.detach_head = detach_head
2896 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002897 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002898
2899 def info(self, project, fmt, *args):
2900 self._messages.append(_InfoMessage(project, fmt % args))
2901
2902 def fail(self, project, err=None):
2903 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002904 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002905
2906 def later1(self, project, what):
2907 self._later_queue1.append(_Later(project, what))
2908
2909 def later2(self, project, what):
2910 self._later_queue2.append(_Later(project, what))
2911
2912 def Finish(self):
2913 self._PrintMessages()
2914 self._RunLater()
2915 self._PrintMessages()
2916 return self.clean
2917
David Rileye0684ad2017-04-05 00:02:59 -07002918 def Recently(self):
2919 recent_clean = self.recent_clean
2920 self.recent_clean = True
2921 return recent_clean
2922
2923 def _MarkUnclean(self):
2924 self.clean = False
2925 self.recent_clean = False
2926
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002927 def _RunLater(self):
2928 for q in ['_later_queue1', '_later_queue2']:
2929 if not self._RunQueue(q):
2930 return
2931
2932 def _RunQueue(self, queue):
2933 for m in getattr(self, queue):
2934 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002935 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002936 return False
2937 setattr(self, queue, [])
2938 return True
2939
2940 def _PrintMessages(self):
2941 for m in self._messages:
2942 m.Print(self)
2943 for m in self._failures:
2944 m.Print(self)
2945
2946 self._messages = []
2947 self._failures = []
2948
2949
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002950class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002951
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002952 """A special project housed under .repo.
2953 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002954
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002955 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002956 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002957 manifest=manifest,
2958 name=name,
2959 gitdir=gitdir,
2960 objdir=gitdir,
2961 worktree=worktree,
2962 remote=RemoteSpec('origin'),
2963 relpath='.repo/%s' % name,
2964 revisionExpr='refs/heads/master',
2965 revisionId=None,
2966 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002967
2968 def PreSync(self):
2969 if self.Exists:
2970 cb = self.CurrentBranch
2971 if cb:
2972 base = self.GetBranch(cb).merge
2973 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002974 self.revisionExpr = base
2975 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002976
Martin Kelly224a31a2017-07-10 14:46:25 -07002977 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02002978 """ Prepare MetaProject for manifest branch switch
2979 """
2980
2981 # detach and delete manifest branch, allowing a new
2982 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01002983 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07002984 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02002985 syncbuf.Finish()
2986
2987 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002988 ['update-ref', '-d', 'refs/heads/default'],
2989 capture_stdout=True,
2990 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02002991
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002992 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07002993 def LastFetch(self):
2994 try:
2995 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
2996 return os.path.getmtime(fh)
2997 except OSError:
2998 return 0
2999
3000 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003001 def HasChanges(self):
3002 """Has the remote received new commits not yet checked out?
3003 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003004 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003005 return False
3006
David Pursehouse8a68ff92012-09-24 12:15:13 +09003007 all_refs = self.bare_ref.all
3008 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003009 head = self.work_git.GetHead()
3010 if head.startswith(R_HEADS):
3011 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003012 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003013 except KeyError:
3014 head = None
3015
3016 if revid == head:
3017 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003018 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003019 return True
3020 return False