blob: 1b6ab0af935cc83b8541bcbee3a2913a6b5d255f [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
Ningning Xiac2fbc782016-08-22 14:24:39 -070034 ID_RE, RefSpec
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Ningning Xiac2fbc782016-08-22 14:24:39 -070036from error import CacheApplyError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080037from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080038from error import NoManifestException
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070039from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070040
Shawn O. Pearced237b692009-04-17 18:49:50 -070041from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070042
David Pursehouse59bbb582013-05-17 10:49:33 +090043from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040044if is_python3():
45 import urllib.parse
46else:
47 import imp
48 import urlparse
49 urllib = imp.new_module('urllib')
50 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090051 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053052 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090053 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053054
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070055
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070056def _lwrite(path, content):
57 lock = '%s.lock' % path
58
Chirayu Desai303a82f2014-08-19 22:57:17 +053059 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070060 try:
61 fd.write(content)
62 finally:
63 fd.close()
64
65 try:
66 os.rename(lock, path)
67 except OSError:
68 os.remove(lock)
69 raise
70
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070071
Shawn O. Pearce48244782009-04-16 08:25:57 -070072def _error(fmt, *args):
73 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070074 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070075
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070076
David Pursehousef33929d2015-08-24 14:39:14 +090077def _warn(fmt, *args):
78 msg = fmt % args
79 print('warn: %s' % msg, file=sys.stderr)
80
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070081
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070082def not_rev(r):
83 return '^' + r
84
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070085
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080086def sq(r):
87 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080088
Jonathan Nieder93719792015-03-17 11:29:58 -070089_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070090
91
Jonathan Nieder93719792015-03-17 11:29:58 -070092def _ProjectHooks():
93 """List the hooks present in the 'hooks' directory.
94
95 These hooks are project hooks and are copied to the '.git/hooks' directory
96 of all subprojects.
97
98 This function caches the list of hooks (based on the contents of the
99 'repo/hooks' directory) on the first call.
100
101 Returns:
102 A list of absolute paths to all of the files in the hooks directory.
103 """
104 global _project_hook_list
105 if _project_hook_list is None:
106 d = os.path.realpath(os.path.abspath(os.path.dirname(__file__)))
107 d = os.path.join(d, 'hooks')
108 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
109 return _project_hook_list
110
111
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700112class DownloadedChange(object):
113 _commit_cache = None
114
115 def __init__(self, project, base, change_id, ps_id, commit):
116 self.project = project
117 self.base = base
118 self.change_id = change_id
119 self.ps_id = ps_id
120 self.commit = commit
121
122 @property
123 def commits(self):
124 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700125 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
126 '--abbrev-commit',
127 '--pretty=oneline',
128 '--reverse',
129 '--date-order',
130 not_rev(self.base),
131 self.commit,
132 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700133 return self._commit_cache
134
135
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700136class ReviewableBranch(object):
137 _commit_cache = None
138
139 def __init__(self, project, branch, base):
140 self.project = project
141 self.branch = branch
142 self.base = base
143
144 @property
145 def name(self):
146 return self.branch.name
147
148 @property
149 def commits(self):
150 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700151 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
152 '--abbrev-commit',
153 '--pretty=oneline',
154 '--reverse',
155 '--date-order',
156 not_rev(self.base),
157 R_HEADS + self.name,
158 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700159 return self._commit_cache
160
161 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800162 def unabbrev_commits(self):
163 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700164 for commit in self.project.bare_git.rev_list(not_rev(self.base),
165 R_HEADS + self.name,
166 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800167 r[commit[0:8]] = commit
168 return r
169
170 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700171 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700172 return self.project.bare_git.log('--pretty=format:%cd',
173 '-n', '1',
174 R_HEADS + self.name,
175 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700176
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700177 def UploadForReview(self, people,
178 auto_topic=False,
179 draft=False,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200180 private=False,
Vadim Bendebury329651b2018-10-31 13:48:01 -0700181 notify=None,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200182 wip=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700183 dest_branch=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800184 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700185 people,
Brian Harring435370c2012-07-28 15:37:04 -0700186 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400187 draft=draft,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200188 private=private,
Vadim Bendebury329651b2018-10-31 13:48:01 -0700189 notify=notify,
Changcheng Xiao7da6f862017-08-02 16:55:03 +0200190 wip=wip,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400191 dest_branch=dest_branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700192
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700193 def GetPublishedRefs(self):
194 refs = {}
195 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700196 self.branch.remote.SshReviewUrl(self.project.UserEmail),
197 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700198 for line in output.split('\n'):
199 try:
200 (sha, ref) = line.split()
201 refs[sha] = ref
202 except ValueError:
203 pass
204
205 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700206
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700207
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700208class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700209
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700210 def __init__(self, config):
211 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100212 self.project = self.printer('header', attr='bold')
213 self.branch = self.printer('header', attr='bold')
214 self.nobranch = self.printer('nobranch', fg='red')
215 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700216
Anthony King7bdac712014-07-16 12:56:40 +0100217 self.added = self.printer('added', fg='green')
218 self.changed = self.printer('changed', fg='red')
219 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700220
221
222class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700223
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700224 def __init__(self, config):
225 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100226 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700227
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700228
Anthony King7bdac712014-07-16 12:56:40 +0100229class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700230
James W. Mills24c13082012-04-12 15:04:13 -0500231 def __init__(self, name, value, keep):
232 self.name = name
233 self.value = value
234 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700236
Anthony King7bdac712014-07-16 12:56:40 +0100237class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700238
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800239 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700240 self.src = src
241 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800242 self.abs_src = abssrc
243 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700244
245 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800246 src = self.abs_src
247 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700248 # copy file if it does not exist or is out of date
249 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
250 try:
251 # remove existing file first, since it might be read-only
252 if os.path.exists(dest):
253 os.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400254 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200255 dest_dir = os.path.dirname(dest)
256 if not os.path.isdir(dest_dir):
257 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700258 shutil.copy(src, dest)
259 # make the file read-only
260 mode = os.stat(dest)[stat.ST_MODE]
261 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
262 os.chmod(dest, mode)
263 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700264 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700265
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700266
Anthony King7bdac712014-07-16 12:56:40 +0100267class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700268
Wink Saville4c426ef2015-06-03 08:05:17 -0700269 def __init__(self, git_worktree, src, dest, relsrc, absdest):
270 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500271 self.src = src
272 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700273 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 self.abs_dest = absdest
275
Wink Saville4c426ef2015-06-03 08:05:17 -0700276 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500277 # link file if it does not exist or is out of date
Wink Saville4c426ef2015-06-03 08:05:17 -0700278 if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500279 try:
280 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800281 if os.path.lexists(absDest):
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 os.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700284 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500285 if not os.path.isdir(dest_dir):
286 os.makedirs(dest_dir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700287 os.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500288 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700289 _error('Cannot link file %s to %s', relSrc, absDest)
290
291 def _Link(self):
292 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
293 on the src linking all of the files in the source in to the destination
294 directory.
295 """
296 # We use the absSrc to handle the situation where the current directory
297 # is not the root of the repo
298 absSrc = os.path.join(self.git_worktree, self.src)
299 if os.path.exists(absSrc):
300 # Entity exists so just a simple one to one link operation
301 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
302 else:
303 # Entity doesn't exist assume there is a wild card
304 absDestDir = self.abs_dest
305 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
306 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700307 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700308 else:
309 absSrcFiles = glob.glob(absSrc)
310 for absSrcFile in absSrcFiles:
311 # Create a releative path from source dir to destination dir
312 absSrcDir = os.path.dirname(absSrcFile)
313 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
314
315 # Get the source file name
316 srcFile = os.path.basename(absSrcFile)
317
318 # Now form the final full paths to srcFile. They will be
319 # absolute for the desintaiton and relative for the srouce.
320 absDest = os.path.join(absDestDir, srcFile)
321 relSrc = os.path.join(relSrcDir, srcFile)
322 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500323
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700324
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700325class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700326
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327 def __init__(self,
328 name,
Anthony King7bdac712014-07-16 12:56:40 +0100329 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700330 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100331 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700332 revision=None,
333 orig_name=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700334 self.name = name
335 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700336 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700337 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100338 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700339 self.orig_name = orig_name
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:
Mark E. Hamiltonf9fe3e12016-02-23 18:10:42 -0700697 self.worktree = os.path.normpath(worktree.replace('\\', '/'))
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800698 else:
699 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700700 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700701 self.revisionExpr = revisionExpr
702
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700703 if revisionId is None \
704 and revisionExpr \
705 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700706 self.revisionId = revisionExpr
707 else:
708 self.revisionId = revisionId
709
Mike Pontillod3153822012-02-28 11:53:24 -0800710 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700711 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700712 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800713 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900714 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700715 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800716 self.parent = parent
717 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900718 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800719 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800720
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700721 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700722 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500723 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500724 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700725 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
726 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700727
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800728 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700729 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800730 else:
731 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700732 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700733 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700734 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400735 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700736 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700737
Doug Anderson37282b42011-03-04 11:54:18 -0800738 # This will be filled in if a project is later identified to be the
739 # project containing repo hooks.
740 self.enabled_repo_hooks = []
741
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800743 def Derived(self):
744 return self.is_derived
745
746 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700747 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600748 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700749
750 @property
751 def CurrentBranch(self):
752 """Obtain the name of the currently checked out branch.
753 The branch name omits the 'refs/heads/' prefix.
754 None is returned if the project is on a detached HEAD.
755 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700756 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700757 if b.startswith(R_HEADS):
758 return b[len(R_HEADS):]
759 return None
760
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700761 def IsRebaseInProgress(self):
762 w = self.worktree
763 g = os.path.join(w, '.git')
764 return os.path.exists(os.path.join(g, 'rebase-apply')) \
765 or os.path.exists(os.path.join(g, 'rebase-merge')) \
766 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200767
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700768 def IsDirty(self, consider_untracked=True):
769 """Is the working directory modified in some way?
770 """
771 self.work_git.update_index('-q',
772 '--unmerged',
773 '--ignore-missing',
774 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900775 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700776 return True
777 if self.work_git.DiffZ('diff-files'):
778 return True
779 if consider_untracked and self.work_git.LsOthers():
780 return True
781 return False
782
783 _userident_name = None
784 _userident_email = None
785
786 @property
787 def UserName(self):
788 """Obtain the user's personal name.
789 """
790 if self._userident_name is None:
791 self._LoadUserIdentity()
792 return self._userident_name
793
794 @property
795 def UserEmail(self):
796 """Obtain the user's email address. This is very likely
797 to be their Gerrit login.
798 """
799 if self._userident_email is None:
800 self._LoadUserIdentity()
801 return self._userident_email
802
803 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900804 u = self.bare_git.var('GIT_COMMITTER_IDENT')
805 m = re.compile("^(.*) <([^>]*)> ").match(u)
806 if m:
807 self._userident_name = m.group(1)
808 self._userident_email = m.group(2)
809 else:
810 self._userident_name = ''
811 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700812
813 def GetRemote(self, name):
814 """Get the configuration for a single remote.
815 """
816 return self.config.GetRemote(name)
817
818 def GetBranch(self, name):
819 """Get the configuration for a single branch.
820 """
821 return self.config.GetBranch(name)
822
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700823 def GetBranches(self):
824 """Get all existing local branches.
825 """
826 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900827 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700829
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530830 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700831 if name.startswith(R_HEADS):
832 name = name[len(R_HEADS):]
833 b = self.GetBranch(name)
834 b.current = name == current
835 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900836 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837 heads[name] = b
838
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530839 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700840 if name.startswith(R_PUB):
841 name = name[len(R_PUB):]
842 b = heads.get(name)
843 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900844 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700845
846 return heads
847
Colin Cross5acde752012-03-28 20:15:45 -0700848 def MatchesGroups(self, manifest_groups):
849 """Returns true if the manifest groups specified at init should cause
850 this project to be synced.
851 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700852 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700853
854 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700855 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700856 manifest_groups: "-group1,group2"
857 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500858
859 The special manifest group "default" will match any project that
860 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700861 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500862 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700864 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500865 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700866
Conley Owens971de8e2012-04-16 10:36:08 -0700867 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 for group in expanded_manifest_groups:
869 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700870 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700871 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700872 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700873
Conley Owens971de8e2012-04-16 10:36:08 -0700874 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700875
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700876# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700877 def UncommitedFiles(self, get_all=True):
878 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700879
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700880 Args:
881 get_all: a boolean, if True - get information about all different
882 uncommitted files. If False - return as soon as any kind of
883 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500884 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700885 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500886 self.work_git.update_index('-q',
887 '--unmerged',
888 '--ignore-missing',
889 '--refresh')
890 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700891 details.append("rebase in progress")
892 if not get_all:
893 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500894
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700895 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
896 if changes:
897 details.extend(changes)
898 if not get_all:
899 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500900
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700901 changes = self.work_git.DiffZ('diff-files').keys()
902 if changes:
903 details.extend(changes)
904 if not get_all:
905 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500906
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700907 changes = self.work_git.LsOthers()
908 if changes:
909 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500910
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700911 return details
912
913 def HasChanges(self):
914 """Returns true if there are uncommitted changes.
915 """
916 if self.UncommitedFiles(get_all=False):
917 return True
918 else:
919 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500920
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600921 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700922 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200923
924 Args:
925 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600926 quiet: If True then only print the project name. Do not print
927 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 """
929 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700930 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200931 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700932 print(file=output_redir)
933 print('project %s/' % self.relpath, file=output_redir)
934 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 return
936
937 self.work_git.update_index('-q',
938 '--unmerged',
939 '--ignore-missing',
940 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700941 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700942 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
943 df = self.work_git.DiffZ('diff-files')
944 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100945 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700946 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700947
948 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700949 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200950 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700951 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700952
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600953 if quiet:
954 out.nl()
955 return 'DIRTY'
956
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700957 branch = self.CurrentBranch
958 if branch is None:
959 out.nobranch('(*** NO BRANCH ***)')
960 else:
961 out.branch('branch %s', branch)
962 out.nl()
963
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700964 if rb:
965 out.important('prior sync failed; rebase still in progress')
966 out.nl()
967
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700968 paths = list()
969 paths.extend(di.keys())
970 paths.extend(df.keys())
971 paths.extend(do)
972
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530973 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900974 try:
975 i = di[p]
976 except KeyError:
977 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700978
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900979 try:
980 f = df[p]
981 except KeyError:
982 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200983
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900984 if i:
985 i_status = i.status.upper()
986 else:
987 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700988
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900989 if f:
990 f_status = f.status.lower()
991 else:
992 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700993
994 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800995 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700996 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700997 else:
998 line = ' %s%s\t%s' % (i_status, f_status, p)
999
1000 if i and not f:
1001 out.added('%s', line)
1002 elif (i and f) or (not i and f):
1003 out.changed('%s', line)
1004 elif not i and not f:
1005 out.untracked('%s', line)
1006 else:
1007 out.write('%s', line)
1008 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001009
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001010 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001011
pelyad67872d2012-03-28 14:49:58 +03001012 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001013 """Prints the status of the repository to stdout.
1014 """
1015 out = DiffColoring(self.config)
1016 cmd = ['diff']
1017 if out.is_on:
1018 cmd.append('--color')
1019 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001020 if absolute_paths:
1021 cmd.append('--src-prefix=a/%s/' % self.relpath)
1022 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001023 cmd.append('--')
1024 p = GitCommand(self,
1025 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001026 capture_stdout=True,
1027 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001028 has_diff = False
1029 for line in p.process.stdout:
1030 if not has_diff:
1031 out.nl()
1032 out.project('project %s/' % self.relpath)
1033 out.nl()
1034 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001035 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001036 p.Wait()
1037
1038
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001039# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001040
David Pursehouse8a68ff92012-09-24 12:15:13 +09001041 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001042 """Was the branch published (uploaded) for code review?
1043 If so, returns the SHA-1 hash of the last published
1044 state for the branch.
1045 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001046 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001047 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001048 try:
1049 return self.bare_git.rev_parse(key)
1050 except GitError:
1051 return None
1052 else:
1053 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001055 except KeyError:
1056 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001057
David Pursehouse8a68ff92012-09-24 12:15:13 +09001058 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059 """Prunes any stale published refs.
1060 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001061 if all_refs is None:
1062 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 heads = set()
1064 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301065 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001066 if name.startswith(R_HEADS):
1067 heads.add(name)
1068 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001069 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301071 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072 n = name[len(R_PUB):]
1073 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001074 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001075
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001076 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001077 """List any branches which can be uploaded for review.
1078 """
1079 heads = {}
1080 pubed = {}
1081
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301082 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001083 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001084 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001085 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001086 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
1088 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301089 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001090 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001092 if selected_branch and branch != selected_branch:
1093 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001094
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001095 rb = self.GetUploadableBranch(branch)
1096 if rb:
1097 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001098 return ready
1099
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001100 def GetUploadableBranch(self, branch_name):
1101 """Get a single uploadable branch, or None.
1102 """
1103 branch = self.GetBranch(branch_name)
1104 base = branch.LocalMerge
1105 if branch.LocalMerge:
1106 rb = ReviewableBranch(self, branch, base)
1107 if rb.commits:
1108 return rb
1109 return None
1110
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001111 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001112 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001113 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001114 draft=False,
Changcheng Xiao7da6f862017-08-02 16:55:03 +02001115 private=False,
Vadim Bendebury329651b2018-10-31 13:48:01 -07001116 notify=None,
Changcheng Xiao7da6f862017-08-02 16:55:03 +02001117 wip=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001118 dest_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001119 """Uploads the named branch for code review.
1120 """
1121 if branch is None:
1122 branch = self.CurrentBranch
1123 if branch is None:
1124 raise GitError('not currently on a branch')
1125
1126 branch = self.GetBranch(branch)
1127 if not branch.LocalMerge:
1128 raise GitError('branch %s does not track a remote' % branch.name)
1129 if not branch.remote.review:
1130 raise GitError('remote %s has no review url' % branch.remote.name)
1131
Bryan Jacobsf609f912013-05-06 13:36:24 -04001132 if dest_branch is None:
1133 dest_branch = self.dest_branch
1134 if dest_branch is None:
1135 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001136 if not dest_branch.startswith(R_HEADS):
1137 dest_branch = R_HEADS + dest_branch
1138
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001139 if not branch.remote.projectname:
1140 branch.remote.projectname = self.name
1141 branch.remote.Save()
1142
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001143 url = branch.remote.ReviewUrl(self.UserEmail)
1144 if url is None:
1145 raise UploadError('review not configured')
1146 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001147
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001148 if url.startswith('ssh://'):
Jonathan Nieder4ad4c462018-11-05 13:21:52 -08001149 cmd.append('--receive-pack=gerrit receive-pack')
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001150
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001151 cmd.append(url)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001152
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001153 if dest_branch.startswith(R_HEADS):
1154 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001155
1156 upload_type = 'for'
1157 if draft:
1158 upload_type = 'drafts'
1159
1160 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1161 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001162 if auto_topic:
1163 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao7da6f862017-08-02 16:55:03 +02001164
Jonathan Nieder4ad4c462018-11-05 13:21:52 -08001165 opts = ['r=%s' % p for p in people[0]]
1166 opts += ['cc=%s' % p for p in people[1]]
Vadim Bendebury329651b2018-10-31 13:48:01 -07001167 if notify:
1168 opts += ['notify=' + notify]
Jonathan Nieder4ad4c462018-11-05 13:21:52 -08001169 if private:
1170 opts += ['private']
1171 if wip:
1172 opts += ['wip']
1173 if opts:
1174 ref_spec = ref_spec + '%' + ','.join(opts)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001175 cmd.append(ref_spec)
1176
Anthony King7bdac712014-07-16 12:56:40 +01001177 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001178 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001179
1180 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1181 self.bare_git.UpdateRef(R_PUB + branch.name,
1182 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001183 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001184
1185
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001186# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001187
Julien Campergue335f5ef2013-10-16 11:02:35 +02001188 def _ExtractArchive(self, tarpath, path=None):
1189 """Extract the given tar on its current location
1190
1191 Args:
1192 - tarpath: The path to the actual tar file
1193
1194 """
1195 try:
1196 with tarfile.open(tarpath, 'r') as tar:
1197 tar.extractall(path=path)
1198 return True
1199 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001200 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001201 return False
1202
Ningning Xiac2fbc782016-08-22 14:24:39 -07001203 def CachePopulate(self, cache_dir, url):
1204 """Populate cache in the cache_dir.
1205
1206 Args:
1207 cache_dir: Directory to cache git files from Google Storage.
1208 url: Git url of current repository.
1209
1210 Raises:
1211 CacheApplyError if it fails to populate the git cache.
1212 """
1213 cmd = ['cache', 'populate', '--ignore_locks', '-v',
1214 '--cache-dir', cache_dir, url]
1215
1216 if GitCommand(self, cmd, cwd=cache_dir).Wait() != 0:
1217 raise CacheApplyError('Failed to populate cache. cache_dir: %s '
1218 'url: %s' % (cache_dir, url))
1219
1220 def CacheExists(self, cache_dir, url):
1221 """Check the existence of the cache files.
1222
1223 Args:
1224 cache_dir: Directory to cache git files.
1225 url: Git url of current repository.
1226
1227 Raises:
1228 CacheApplyError if the cache files do not exist.
1229 """
1230 cmd = ['cache', 'exists', '--quiet', '--cache-dir', cache_dir, url]
1231
1232 exist = GitCommand(self, cmd, cwd=self.gitdir, capture_stdout=True)
1233 if exist.Wait() != 0:
1234 raise CacheApplyError('Failed to execute git cache exists cmd. '
1235 'cache_dir: %s url: %s' % (cache_dir, url))
1236
1237 if not exist.stdout or not exist.stdout.strip():
1238 raise CacheApplyError('Failed to find cache. cache_dir: %s '
1239 'url: %s' % (cache_dir, url))
1240 return exist.stdout.strip()
1241
1242 def CacheApply(self, cache_dir):
1243 """Apply git cache files populated from Google Storage buckets.
1244
1245 Args:
1246 cache_dir: Directory to cache git files.
1247
1248 Raises:
1249 CacheApplyError if it fails to apply git caches.
1250 """
1251 remote = self.GetRemote(self.remote.name)
1252
1253 self.CachePopulate(cache_dir, remote.url)
1254
1255 mirror_dir = self.CacheExists(cache_dir, remote.url)
1256
1257 refspec = RefSpec(True, 'refs/heads/*',
1258 'refs/remotes/%s/*' % remote.name)
1259
1260 fetch_cache_cmd = ['fetch', mirror_dir, str(refspec)]
1261 if GitCommand(self, fetch_cache_cmd, self.gitdir).Wait() != 0:
1262 raise CacheApplyError('Failed to fetch refs %s from %s' %
1263 (mirror_dir, str(refspec)))
1264
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001265 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001266 quiet=False,
1267 is_new=None,
1268 current_branch_only=False,
1269 force_sync=False,
1270 clone_bundle=True,
1271 no_tags=False,
1272 archive=False,
1273 optimized_fetch=False,
Ningning Xiac2fbc782016-08-22 14:24:39 -07001274 prune=False,
1275 cache_dir=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001276 """Perform only the network IO portion of the sync process.
1277 Local working directory/branch state is not affected.
1278 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001279 if archive and not isinstance(self, MetaProject):
1280 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001281 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001282 return False
1283
1284 name = self.relpath.replace('\\', '/')
1285 name = name.replace('/', '_')
1286 tarpath = '%s.tar' % name
1287 topdir = self.manifest.topdir
1288
1289 try:
1290 self._FetchArchive(tarpath, cwd=topdir)
1291 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001292 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001293 return False
1294
1295 # From now on, we only need absolute tarpath
1296 tarpath = os.path.join(topdir, tarpath)
1297
1298 if not self._ExtractArchive(tarpath, path=topdir):
1299 return False
1300 try:
1301 os.remove(tarpath)
1302 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001303 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001304 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001305 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001306 if is_new is None:
1307 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001308 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001309 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001310 else:
1311 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001312 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001313
1314 if is_new:
1315 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1316 try:
1317 fd = open(alt, 'rb')
1318 try:
1319 alt_dir = fd.readline().rstrip()
1320 finally:
1321 fd.close()
1322 except IOError:
1323 alt_dir = None
1324 else:
1325 alt_dir = None
1326
Ningning Xiac2fbc782016-08-22 14:24:39 -07001327 applied_cache = False
1328 # If cache_dir is provided, and it's a new repository without
1329 # alternative_dir, bootstrap this project repo with the git
1330 # cache files.
1331 if cache_dir is not None and is_new and alt_dir is None:
1332 try:
1333 self.CacheApply(cache_dir)
1334 applied_cache = True
1335 is_new = False
1336 except CacheApplyError as e:
1337 _error('Could not apply git cache: %s', e)
1338 _error('Please check if you have the right GS credentials.')
1339 _error('Please check if the cache files exist in GS.')
1340
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001341 if clone_bundle \
Ningning Xiac2fbc782016-08-22 14:24:39 -07001342 and not applied_cache \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001343 and alt_dir is None \
1344 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001345 is_new = False
1346
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001347 if not current_branch_only:
1348 if self.sync_c:
1349 current_branch_only = True
1350 elif not self.manifest._loaded:
1351 # Manifest cannot check defaults until it syncs.
1352 current_branch_only = False
1353 elif self.manifest.default.sync_c:
1354 current_branch_only = True
1355
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001356 if self.clone_depth:
1357 depth = self.clone_depth
1358 else:
1359 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1360
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001361 need_to_fetch = not (optimized_fetch and
1362 (ID_RE.match(self.revisionExpr) and
1363 self._CheckForSha1()))
1364 if (need_to_fetch and
1365 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1366 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001367 no_tags=no_tags, prune=prune, depth=depth)):
Anthony King7bdac712014-07-16 12:56:40 +01001368 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001369
1370 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001371 self._InitMRef()
1372 else:
1373 self._InitMirrorHead()
1374 try:
1375 os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
1376 except OSError:
1377 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001378 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001379
1380 def PostRepoUpgrade(self):
1381 self._InitHooks()
1382
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001383 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001384 if self.manifest.isGitcClient:
1385 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001386 for copyfile in self.copyfiles:
1387 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001388 for linkfile in self.linkfiles:
1389 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001390
Julien Camperguedd654222014-01-09 16:21:37 +01001391 def GetCommitRevisionId(self):
1392 """Get revisionId of a commit.
1393
1394 Use this method instead of GetRevisionId to get the id of the commit rather
1395 than the id of the current git object (for example, a tag)
1396
1397 """
1398 if not self.revisionExpr.startswith(R_TAGS):
1399 return self.GetRevisionId(self._allrefs)
1400
1401 try:
1402 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1403 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001404 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1405 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001406
David Pursehouse8a68ff92012-09-24 12:15:13 +09001407 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001408 if self.revisionId:
1409 return self.revisionId
1410
1411 rem = self.GetRemote(self.remote.name)
1412 rev = rem.ToLocal(self.revisionExpr)
1413
David Pursehouse8a68ff92012-09-24 12:15:13 +09001414 if all_refs is not None and rev in all_refs:
1415 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001416
1417 try:
1418 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1419 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001420 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1421 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001422
Kevin Degiabaa7f32014-11-12 11:27:45 -07001423 def Sync_LocalHalf(self, syncbuf, force_sync=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001424 """Perform only the local IO portion of the sync process.
1425 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001426 """
Kevin Degiabaa7f32014-11-12 11:27:45 -07001427 self._InitWorkTree(force_sync=force_sync)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001428 all_refs = self.bare_ref.all
1429 self.CleanPublishedCache(all_refs)
1430 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001431
David Pursehouse1d947b32012-10-25 12:23:11 +09001432 def _doff():
1433 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001434 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001435
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001436 head = self.work_git.GetHead()
1437 if head.startswith(R_HEADS):
1438 branch = head[len(R_HEADS):]
1439 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001440 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001441 except KeyError:
1442 head = None
1443 else:
1444 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001445
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001446 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001447 # Currently on a detached HEAD. The user is assumed to
1448 # not have any local modifications worth worrying about.
1449 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001450 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001451 syncbuf.fail(self, _PriorSyncFailedError())
1452 return
1453
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001454 if head == revid:
1455 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001456 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001457 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001458 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001459 # The copy/linkfile config may have changed.
1460 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001461 return
1462 else:
1463 lost = self._revlist(not_rev(revid), HEAD)
1464 if lost:
1465 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001466
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001467 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001468 self._Checkout(revid, quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001469 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001470 syncbuf.fail(self, e)
1471 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001472 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001473 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001474
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001475 if head == revid:
1476 # No changes; don't do anything further.
1477 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001478 # The copy/linkfile config may have changed.
1479 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001480 return
1481
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001482 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001483
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001484 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001485 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001486 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001487 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001488 syncbuf.info(self,
1489 "leaving %s; does not track upstream",
1490 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001491 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001492 self._Checkout(revid, quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001493 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001494 syncbuf.fail(self, e)
1495 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001496 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001497 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001498
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001499 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001500 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001501 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001502 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001503 if not_merged:
1504 if upstream_gain:
1505 # The user has published this branch and some of those
1506 # commits are not yet merged upstream. We do not want
1507 # to rewrite the published commits so we punt.
1508 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001509 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001510 "branch %s is published (but not merged) and is now "
1511 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001512 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001513 elif pub == head:
1514 # All published commits are merged, and thus we are a
1515 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001516 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001517 syncbuf.later1(self, _doff)
1518 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001520 # Examine the local commits not in the remote. Find the
1521 # last one attributed to this user, if any.
1522 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001523 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001524 last_mine = None
1525 cnt_mine = 0
1526 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301527 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001528 if committer_email == self.UserEmail:
1529 last_mine = commit_id
1530 cnt_mine += 1
1531
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001532 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001533 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001534
1535 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001536 syncbuf.fail(self, _DirtyError())
1537 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001538
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001539 # If the upstream switched on us, warn the user.
1540 #
1541 if branch.merge != self.revisionExpr:
1542 if branch.merge and self.revisionExpr:
1543 syncbuf.info(self,
1544 'manifest switched %s...%s',
1545 branch.merge,
1546 self.revisionExpr)
1547 elif branch.merge:
1548 syncbuf.info(self,
1549 'manifest no longer tracks %s',
1550 branch.merge)
1551
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001552 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001554 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001556 syncbuf.info(self,
1557 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001558 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001559
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001560 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001561 if not ID_RE.match(self.revisionExpr):
1562 # in case of manifest sync the revisionExpr might be a SHA1
1563 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001564 if not branch.merge.startswith('refs/'):
1565 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001566 branch.Save()
1567
Mike Pontillod3153822012-02-28 11:53:24 -08001568 if cnt_mine > 0 and self.rebase:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001569 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001570 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001571 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001572 syncbuf.later2(self, _dorebase)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001573 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001574 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001575 self._ResetHard(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001576 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001577 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001578 syncbuf.fail(self, e)
1579 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001580 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001581 syncbuf.later1(self, _doff)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001582
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001583 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001584 # dest should already be an absolute path, but src is project relative
1585 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001586 abssrc = os.path.join(self.worktree, src)
1587 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001588
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001589 def AddLinkFile(self, src, dest, absdest):
1590 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001591 # make src relative path to dest
1592 absdestdir = os.path.dirname(absdest)
1593 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001594 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001595
James W. Mills24c13082012-04-12 15:04:13 -05001596 def AddAnnotation(self, name, value, keep):
1597 self.annotations.append(_Annotation(name, value, keep))
1598
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001599 def DownloadPatchSet(self, change_id, patch_id):
1600 """Download a single patch set of a single change to FETCH_HEAD.
1601 """
1602 remote = self.GetRemote(self.remote.name)
1603
1604 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001605 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001606 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001607 if GitCommand(self, cmd, bare=True).Wait() != 0:
1608 return None
1609 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001610 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001611 change_id,
1612 patch_id,
1613 self.bare_git.rev_parse('FETCH_HEAD'))
1614
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001615
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001616# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001617
Simran Basib9a1b732015-08-20 12:19:28 -07001618 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001619 """Create a new branch off the manifest's revision.
1620 """
Simran Basib9a1b732015-08-20 12:19:28 -07001621 if not branch_merge:
1622 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001623 head = self.work_git.GetHead()
1624 if head == (R_HEADS + name):
1625 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001626
David Pursehouse8a68ff92012-09-24 12:15:13 +09001627 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001628 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001629 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001630 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001631 capture_stdout=True,
1632 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001633
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001634 branch = self.GetBranch(name)
1635 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001636 branch.merge = branch_merge
1637 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1638 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001639 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001640
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001641 if head.startswith(R_HEADS):
1642 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001643 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001644 except KeyError:
1645 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001646 if revid and head and revid == head:
1647 ref = os.path.join(self.gitdir, R_HEADS + name)
1648 try:
1649 os.makedirs(os.path.dirname(ref))
1650 except OSError:
1651 pass
1652 _lwrite(ref, '%s\n' % revid)
1653 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1654 'ref: %s%s\n' % (R_HEADS, name))
1655 branch.Save()
1656 return True
1657
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001658 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001659 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001660 capture_stdout=True,
1661 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001662 branch.Save()
1663 return True
1664 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001665
Wink Saville02d79452009-04-10 13:01:24 -07001666 def CheckoutBranch(self, name):
1667 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001668
1669 Args:
1670 name: The name of the branch to checkout.
1671
1672 Returns:
1673 True if the checkout succeeded; False if it didn't; None if the branch
1674 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001675 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001676 rev = R_HEADS + name
1677 head = self.work_git.GetHead()
1678 if head == rev:
1679 # Already on the branch
1680 #
1681 return True
Wink Saville02d79452009-04-10 13:01:24 -07001682
David Pursehouse8a68ff92012-09-24 12:15:13 +09001683 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001684 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001685 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001686 except KeyError:
1687 # Branch does not exist in this project
1688 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001689 return None
Wink Saville02d79452009-04-10 13:01:24 -07001690
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001691 if head.startswith(R_HEADS):
1692 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001693 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001694 except KeyError:
1695 head = None
1696
1697 if head == revid:
1698 # Same revision; just update HEAD to point to the new
1699 # target branch, but otherwise take no other action.
1700 #
1701 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1702 'ref: %s%s\n' % (R_HEADS, name))
1703 return True
1704
1705 return GitCommand(self,
1706 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001707 capture_stdout=True,
1708 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001709
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001710 def AbandonBranch(self, name):
1711 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001712
1713 Args:
1714 name: The name of the branch to abandon.
1715
1716 Returns:
1717 True if the abandon succeeded; False if it didn't; None if the branch
1718 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001719 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001720 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001721 all_refs = self.bare_ref.all
1722 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001723 # Doesn't exist
1724 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001725
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001726 head = self.work_git.GetHead()
1727 if head == rev:
1728 # We can't destroy the branch while we are sitting
1729 # on it. Switch to a detached HEAD.
1730 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001731 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001732
David Pursehouse8a68ff92012-09-24 12:15:13 +09001733 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001734 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001735 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1736 '%s\n' % revid)
1737 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001738 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001739
1740 return GitCommand(self,
1741 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001742 capture_stdout=True,
1743 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001744
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001745 def PruneHeads(self):
1746 """Prune any topic branches already merged into upstream.
1747 """
1748 cb = self.CurrentBranch
1749 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001750 left = self._allrefs
1751 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001752 if name.startswith(R_HEADS):
1753 name = name[len(R_HEADS):]
1754 if cb is None or name != cb:
1755 kill.append(name)
1756
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001757 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001758 if cb is not None \
1759 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001760 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001761 self.work_git.DetachHead(HEAD)
1762 kill.append(cb)
1763
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001764 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001765 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001766
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001767 try:
1768 self.bare_git.DetachHead(rev)
1769
1770 b = ['branch', '-d']
1771 b.extend(kill)
1772 b = GitCommand(self, b, bare=True,
1773 capture_stdout=True,
1774 capture_stderr=True)
1775 b.Wait()
1776 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001777 if ID_RE.match(old):
1778 self.bare_git.DetachHead(old)
1779 else:
1780 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001781 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001782
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001783 for branch in kill:
1784 if (R_HEADS + branch) not in left:
1785 self.CleanPublishedCache()
1786 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001787
1788 if cb and cb not in kill:
1789 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001790 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001791
1792 kept = []
1793 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001794 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001795 branch = self.GetBranch(branch)
1796 base = branch.LocalMerge
1797 if not base:
1798 base = rev
1799 kept.append(ReviewableBranch(self, branch, base))
1800 return kept
1801
1802
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001803# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001804
1805 def GetRegisteredSubprojects(self):
1806 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001807
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001808 def rec(subprojects):
1809 if not subprojects:
1810 return
1811 result.extend(subprojects)
1812 for p in subprojects:
1813 rec(p.subprojects)
1814 rec(self.subprojects)
1815 return result
1816
1817 def _GetSubmodules(self):
1818 # Unfortunately we cannot call `git submodule status --recursive` here
1819 # because the working tree might not exist yet, and it cannot be used
1820 # without a working tree in its current implementation.
1821
1822 def get_submodules(gitdir, rev):
1823 # Parse .gitmodules for submodule sub_paths and sub_urls
1824 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1825 if not sub_paths:
1826 return []
1827 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1828 # revision of submodule repository
1829 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1830 submodules = []
1831 for sub_path, sub_url in zip(sub_paths, sub_urls):
1832 try:
1833 sub_rev = sub_revs[sub_path]
1834 except KeyError:
1835 # Ignore non-exist submodules
1836 continue
1837 submodules.append((sub_rev, sub_path, sub_url))
1838 return submodules
1839
1840 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1841 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001842
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001843 def parse_gitmodules(gitdir, rev):
1844 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1845 try:
Anthony King7bdac712014-07-16 12:56:40 +01001846 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1847 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001848 except GitError:
1849 return [], []
1850 if p.Wait() != 0:
1851 return [], []
1852
1853 gitmodules_lines = []
1854 fd, temp_gitmodules_path = tempfile.mkstemp()
1855 try:
1856 os.write(fd, p.stdout)
1857 os.close(fd)
1858 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001859 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1860 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001861 if p.Wait() != 0:
1862 return [], []
1863 gitmodules_lines = p.stdout.split('\n')
1864 except GitError:
1865 return [], []
1866 finally:
1867 os.remove(temp_gitmodules_path)
1868
1869 names = set()
1870 paths = {}
1871 urls = {}
1872 for line in gitmodules_lines:
1873 if not line:
1874 continue
1875 m = re_path.match(line)
1876 if m:
1877 names.add(m.group(1))
1878 paths[m.group(1)] = m.group(2)
1879 continue
1880 m = re_url.match(line)
1881 if m:
1882 names.add(m.group(1))
1883 urls[m.group(1)] = m.group(2)
1884 continue
1885 names = sorted(names)
1886 return ([paths.get(name, '') for name in names],
1887 [urls.get(name, '') for name in names])
1888
1889 def git_ls_tree(gitdir, rev, paths):
1890 cmd = ['ls-tree', rev, '--']
1891 cmd.extend(paths)
1892 try:
Anthony King7bdac712014-07-16 12:56:40 +01001893 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1894 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001895 except GitError:
1896 return []
1897 if p.Wait() != 0:
1898 return []
1899 objects = {}
1900 for line in p.stdout.split('\n'):
1901 if not line.strip():
1902 continue
1903 object_rev, object_path = line.split()[2:4]
1904 objects[object_path] = object_rev
1905 return objects
1906
1907 try:
1908 rev = self.GetRevisionId()
1909 except GitError:
1910 return []
1911 return get_submodules(self.gitdir, rev)
1912
1913 def GetDerivedSubprojects(self):
1914 result = []
1915 if not self.Exists:
1916 # If git repo does not exist yet, querying its submodules will
1917 # mess up its states; so return here.
1918 return result
1919 for rev, path, url in self._GetSubmodules():
1920 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001921 relpath, worktree, gitdir, objdir = \
1922 self.manifest.GetSubprojectPaths(self, name, path)
1923 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001924 if project:
1925 result.extend(project.GetDerivedSubprojects())
1926 continue
David James8d201162013-10-11 17:03:19 -07001927
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001928 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001929 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001930 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001931 review=self.remote.review,
1932 revision=self.remote.revision)
1933 subproject = Project(manifest=self.manifest,
1934 name=name,
1935 remote=remote,
1936 gitdir=gitdir,
1937 objdir=objdir,
1938 worktree=worktree,
1939 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001940 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001941 revisionId=rev,
1942 rebase=self.rebase,
1943 groups=self.groups,
1944 sync_c=self.sync_c,
1945 sync_s=self.sync_s,
1946 parent=self,
1947 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001948 result.append(subproject)
1949 result.extend(subproject.GetDerivedSubprojects())
1950 return result
1951
1952
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001953# Direct Git Commands ##
Chris AtLee2fb64662014-01-16 21:32:33 -05001954 def _CheckForSha1(self):
1955 try:
1956 # if revision (sha or tag) is not present then following function
1957 # throws an error.
1958 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1959 return True
1960 except GitError:
1961 # There is no such persistent revision. We have to fetch it.
1962 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001963
Julien Campergue335f5ef2013-10-16 11:02:35 +02001964 def _FetchArchive(self, tarpath, cwd=None):
1965 cmd = ['archive', '-v', '-o', tarpath]
1966 cmd.append('--remote=%s' % self.remote.url)
1967 cmd.append('--prefix=%s/' % self.relpath)
1968 cmd.append(self.revisionExpr)
1969
1970 command = GitCommand(self, cmd, cwd=cwd,
1971 capture_stdout=True,
1972 capture_stderr=True)
1973
1974 if command.Wait() != 0:
1975 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1976
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001977 def _RemoteFetch(self, name=None,
1978 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001979 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001980 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001981 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001982 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001983 prune=False,
1984 depth=None):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001985
1986 is_sha1 = False
1987 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001988 # The depth should not be used when fetching to a mirror because
1989 # it will result in a shallow repository that cannot be cloned or
1990 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001991 # The repo project should also never be synced with partial depth.
1992 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1993 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001994
Shawn Pearce69e04d82014-01-29 12:48:54 -08001995 if depth:
1996 current_branch_only = True
1997
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001998 if ID_RE.match(self.revisionExpr) is not None:
1999 is_sha1 = True
2000
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002001 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06002002 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002003 # this is a tag and its sha1 value should never change
2004 tag_name = self.revisionExpr[len(R_TAGS):]
2005
2006 if is_sha1 or tag_name is not None:
Chris AtLee2fb64662014-01-16 21:32:33 -05002007 if self._CheckForSha1():
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002008 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08002009 if is_sha1 and not depth:
2010 # When syncing a specific commit and --depth is not set:
2011 # * if upstream is explicitly specified and is not a sha1, fetch only
2012 # upstream as users expect only upstream to be fetch.
2013 # Note: The commit might not be in upstream in which case the sync
2014 # will fail.
2015 # * otherwise, fetch all branches to make sure we end up with the
2016 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02002017 if self.upstream:
2018 current_branch_only = not ID_RE.match(self.upstream)
2019 else:
2020 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002021
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002022 if not name:
2023 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002024
2025 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002026 remote = self.GetRemote(name)
2027 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07002028 ssh_proxy = True
2029
Shawn O. Pearce88443382010-10-08 10:02:09 +02002030 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002031 if alt_dir and 'objects' == os.path.basename(alt_dir):
2032 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002033 packed_refs = os.path.join(self.gitdir, 'packed-refs')
2034 remote = self.GetRemote(name)
2035
David Pursehouse8a68ff92012-09-24 12:15:13 +09002036 all_refs = self.bare_ref.all
2037 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02002038 tmp = set()
2039
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302040 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09002041 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002042 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002043 all_refs[r] = ref_id
2044 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002045 continue
2046
David Pursehouse8a68ff92012-09-24 12:15:13 +09002047 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002048 continue
2049
David Pursehouse8a68ff92012-09-24 12:15:13 +09002050 r = 'refs/_alt/%s' % ref_id
2051 all_refs[r] = ref_id
2052 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002053 tmp.add(r)
2054
Shawn O. Pearce88443382010-10-08 10:02:09 +02002055 tmp_packed = ''
2056 old_packed = ''
2057
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302058 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09002059 line = '%s %s\n' % (all_refs[r], r)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002060 tmp_packed += line
2061 if r not in tmp:
2062 old_packed += line
2063
2064 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002065 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002066 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002067
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002068 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002069
Conley Owensf97e8382015-01-21 11:12:46 -08002070 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002071 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002072 else:
2073 # If this repo has shallow objects, then we don't know which refs have
2074 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2075 # do this with projects that don't have shallow objects, since it is less
2076 # efficient.
2077 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2078 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002079
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002080 if quiet:
2081 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002082 if not self.worktree:
2083 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002084 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002085
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002086 # If using depth then we should not get all the tags since they may
2087 # be outside of the depth.
2088 if no_tags or depth:
2089 cmd.append('--no-tags')
2090 else:
2091 cmd.append('--tags')
2092
David Pursehouse74cfd272015-10-14 10:50:15 +09002093 if prune:
2094 cmd.append('--prune')
2095
Conley Owens80b87fe2014-05-09 17:13:44 -07002096 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002097 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002098 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002099 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002100 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002101 spec.append('tag')
2102 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002103
David Pursehouse403b64e2015-04-27 10:41:33 +09002104 if not self.manifest.IsMirror:
2105 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002106 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002107 # Shallow checkout of a specific commit, fetch from that commit and not
2108 # the heads only as the commit might be deeper in the history.
2109 spec.append(branch)
2110 else:
2111 if is_sha1:
2112 branch = self.upstream
2113 if branch is not None and branch.strip():
2114 if not branch.startswith('refs/'):
2115 branch = R_HEADS + branch
2116 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002117 cmd.extend(spec)
2118
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002119 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002120 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002121 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002122 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002123 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002124 ok = True
2125 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002126 # If needed, run the 'git remote prune' the first time through the loop
2127 elif (not _i and
2128 "error:" in gitcmd.stderr and
2129 "git remote prune" in gitcmd.stderr):
2130 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002131 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002132 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002133 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002134 break
2135 continue
Brian Harring14a66742012-09-28 20:21:57 -07002136 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002137 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2138 # in sha1 mode, we just tried sync'ing from the upstream field; it
2139 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002140 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002141 elif ret < 0:
2142 # Git died with a signal, exit immediately
2143 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002144 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002145
2146 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002147 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002148 if old_packed != '':
2149 _lwrite(packed_refs, old_packed)
2150 else:
2151 os.remove(packed_refs)
2152 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002153
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002154 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002155 # We just synced the upstream given branch; verify we
2156 # got what we wanted, else trigger a second run of all
2157 # refs.
Chris AtLee2fb64662014-01-16 21:32:33 -05002158 if not self._CheckForSha1():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002159 if current_branch_only and depth:
2160 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002161 return self._RemoteFetch(name=name,
2162 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002163 initial=False, quiet=quiet, alt_dir=alt_dir,
2164 depth=None)
2165 else:
2166 # Avoid infinite recursion: sync all branches with depth set to None
2167 return self._RemoteFetch(name=name, current_branch_only=False,
2168 initial=False, quiet=quiet, alt_dir=alt_dir,
2169 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002170
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002171 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002172
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002173 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002174 if initial and \
2175 (self.manifest.manifestProject.config.GetString('repo.depth') or
2176 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002177 return False
2178
2179 remote = self.GetRemote(self.remote.name)
2180 bundle_url = remote.url + '/clone.bundle'
2181 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002182 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2183 'persistent-http',
2184 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002185 return False
2186
2187 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2188 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2189
2190 exist_dst = os.path.exists(bundle_dst)
2191 exist_tmp = os.path.exists(bundle_tmp)
2192
2193 if not initial and not exist_dst and not exist_tmp:
2194 return False
2195
2196 if not exist_dst:
2197 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2198 if not exist_dst:
2199 return False
2200
2201 cmd = ['fetch']
2202 if quiet:
2203 cmd.append('--quiet')
2204 if not self.worktree:
2205 cmd.append('--update-head-ok')
2206 cmd.append(bundle_dst)
2207 for f in remote.fetch:
2208 cmd.append(str(f))
2209 cmd.append('refs/tags/*:refs/tags/*')
2210
2211 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002212 if os.path.exists(bundle_dst):
2213 os.remove(bundle_dst)
2214 if os.path.exists(bundle_tmp):
2215 os.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002216 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002217
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002218 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002219 if os.path.exists(dstPath):
2220 os.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002221
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002222 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002223 if quiet:
2224 cmd += ['--silent']
2225 if os.path.exists(tmpPath):
2226 size = os.stat(tmpPath).st_size
2227 if size >= 1024:
2228 cmd += ['--continue-at', '%d' % (size,)]
2229 else:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002230 os.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002231 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2232 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002233 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002234 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002235 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002236 if srcUrl.startswith('persistent-'):
2237 srcUrl = srcUrl[len('persistent-'):]
2238 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002239
Dave Borowitz137d0132015-01-02 11:12:54 -08002240 if IsTrace():
2241 Trace('%s', ' '.join(cmd))
2242 try:
2243 proc = subprocess.Popen(cmd)
2244 except OSError:
2245 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002246
Dave Borowitz137d0132015-01-02 11:12:54 -08002247 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002248
Dave Borowitz137d0132015-01-02 11:12:54 -08002249 if curlret == 22:
2250 # From curl man page:
2251 # 22: HTTP page not retrieved. The requested url was not found or
2252 # returned another error with the HTTP error code being 400 or above.
2253 # This return code only appears if -f, --fail is used.
2254 if not quiet:
2255 print("Server does not provide clone.bundle; ignoring.",
2256 file=sys.stderr)
2257 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002258
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002259 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002260 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002261 os.rename(tmpPath, dstPath)
2262 return True
2263 else:
2264 os.remove(tmpPath)
2265 return False
2266 else:
2267 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002268
Kris Giesingc8d882a2014-12-23 13:02:32 -08002269 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002270 try:
2271 with open(path) as f:
2272 if f.read(16) == '# v2 git bundle\n':
2273 return True
2274 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002275 if not quiet:
2276 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002277 return False
2278 except OSError:
2279 return False
2280
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002281 def _Checkout(self, rev, quiet=False):
2282 cmd = ['checkout']
2283 if quiet:
2284 cmd.append('-q')
2285 cmd.append(rev)
2286 cmd.append('--')
2287 if GitCommand(self, cmd).Wait() != 0:
2288 if self._allrefs:
2289 raise GitError('%s checkout %s ' % (self.name, rev))
2290
Anthony King7bdac712014-07-16 12:56:40 +01002291 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002292 cmd = ['cherry-pick']
2293 cmd.append(rev)
2294 cmd.append('--')
2295 if GitCommand(self, cmd).Wait() != 0:
2296 if self._allrefs:
2297 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2298
Anthony King7bdac712014-07-16 12:56:40 +01002299 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002300 cmd = ['revert']
2301 cmd.append('--no-edit')
2302 cmd.append(rev)
2303 cmd.append('--')
2304 if GitCommand(self, cmd).Wait() != 0:
2305 if self._allrefs:
2306 raise GitError('%s revert %s ' % (self.name, rev))
2307
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002308 def _ResetHard(self, rev, quiet=True):
2309 cmd = ['reset', '--hard']
2310 if quiet:
2311 cmd.append('-q')
2312 cmd.append(rev)
2313 if GitCommand(self, cmd).Wait() != 0:
2314 raise GitError('%s reset --hard %s ' % (self.name, rev))
2315
Anthony King7bdac712014-07-16 12:56:40 +01002316 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002317 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002318 if onto is not None:
2319 cmd.extend(['--onto', onto])
2320 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002321 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002322 raise GitError('%s rebase %s ' % (self.name, upstream))
2323
Pierre Tardy3d125942012-05-04 12:18:12 +02002324 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002325 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002326 if ffonly:
2327 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002328 if GitCommand(self, cmd).Wait() != 0:
2329 raise GitError('%s merge %s ' % (self.name, head))
2330
Kevin Degiabaa7f32014-11-12 11:27:45 -07002331 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002332 init_git_dir = not os.path.exists(self.gitdir)
2333 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002334 try:
2335 # Initialize the bare repository, which contains all of the objects.
2336 if init_obj_dir:
2337 os.makedirs(self.objdir)
2338 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002339
Kevin Degib1a07b82015-07-27 13:33:43 -06002340 # If we have a separate directory to hold refs, initialize it as well.
2341 if self.objdir != self.gitdir:
2342 if init_git_dir:
2343 os.makedirs(self.gitdir)
2344
2345 if init_obj_dir or init_git_dir:
2346 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2347 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002348 try:
2349 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2350 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002351 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002352 print("Retrying clone after deleting %s" %
2353 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002354 try:
2355 shutil.rmtree(os.path.realpath(self.gitdir))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002356 if self.worktree and os.path.exists(os.path.realpath
2357 (self.worktree)):
Kevin Degiabaa7f32014-11-12 11:27:45 -07002358 shutil.rmtree(os.path.realpath(self.worktree))
2359 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2360 except:
2361 raise e
2362 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002363
Kevin Degi384b3c52014-10-16 16:02:58 -06002364 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002365 mp = self.manifest.manifestProject
2366 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002367
Kevin Degib1a07b82015-07-27 13:33:43 -06002368 if ref_dir or mirror_git:
2369 if not mirror_git:
2370 mirror_git = os.path.join(ref_dir, self.name + '.git')
2371 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2372 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002373
Kevin Degib1a07b82015-07-27 13:33:43 -06002374 if os.path.exists(mirror_git):
2375 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002376
Kevin Degib1a07b82015-07-27 13:33:43 -06002377 elif os.path.exists(repo_git):
2378 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002379
Kevin Degib1a07b82015-07-27 13:33:43 -06002380 else:
2381 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002382
Kevin Degib1a07b82015-07-27 13:33:43 -06002383 if ref_dir:
2384 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2385 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002386
Kevin Degib1a07b82015-07-27 13:33:43 -06002387 self._UpdateHooks()
2388
2389 m = self.manifest.manifestProject.config
2390 for key in ['user.name', 'user.email']:
2391 if m.Has(key, include_defaults=False):
2392 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002393 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002394 if self.manifest.IsMirror:
2395 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002396 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002397 self.config.SetString('core.bare', None)
2398 except Exception:
2399 if init_obj_dir and os.path.exists(self.objdir):
2400 shutil.rmtree(self.objdir)
2401 if init_git_dir and os.path.exists(self.gitdir):
2402 shutil.rmtree(self.gitdir)
2403 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002404
Jimmie Westera0444582012-10-24 13:44:42 +02002405 def _UpdateHooks(self):
2406 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002407 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002408
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002409 def _InitHooks(self):
Jesse Hall672cc492013-11-27 11:17:13 -08002410 hooks = os.path.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002411 if not os.path.exists(hooks):
2412 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002413 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002414 name = os.path.basename(stock_hook)
2415
Victor Boivie65e0f352011-04-18 11:23:29 +02002416 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002417 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002418 # Don't install a Gerrit Code Review hook if this
2419 # project does not appear to use it for reviews.
2420 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002421 # Since the manifest project is one of those, but also
2422 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002423 continue
2424
2425 dst = os.path.join(hooks, name)
2426 if os.path.islink(dst):
2427 continue
2428 if os.path.exists(dst):
2429 if filecmp.cmp(stock_hook, dst, shallow=False):
2430 os.remove(dst)
2431 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002432 _warn("%s: Not replacing locally modified %s hook",
2433 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002434 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002435 try:
Mickaël Salaünb9477bc2012-08-05 13:39:26 +02002436 os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002437 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002438 if e.errno == errno.EPERM:
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002439 raise GitError('filesystem must support symlinks')
2440 else:
2441 raise
2442
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002443 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002444 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002445 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002446 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002447 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002448 remote.review = self.remote.review
2449 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002450
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002451 if self.worktree:
2452 remote.ResetFetch(mirror=False)
2453 else:
2454 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002455 remote.Save()
2456
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002457 def _InitMRef(self):
2458 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002459 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002460
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002461 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002462 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002463
2464 def _InitAnyMRef(self, ref):
2465 cur = self.bare_ref.symref(ref)
2466
2467 if self.revisionId:
2468 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2469 msg = 'manifest set to %s' % self.revisionId
2470 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002471 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002472 else:
2473 remote = self.GetRemote(self.remote.name)
2474 dst = remote.ToLocal(self.revisionExpr)
2475 if cur != dst:
2476 msg = 'manifest set to %s' % self.revisionExpr
2477 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002478
Kevin Degi384b3c52014-10-16 16:02:58 -06002479 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002480 symlink_files = self.shareable_files[:]
2481 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002482 if share_refs:
2483 symlink_files += self.working_tree_files
2484 symlink_dirs += self.working_tree_dirs
2485 to_symlink = symlink_files + symlink_dirs
2486 for name in set(to_symlink):
2487 dst = os.path.realpath(os.path.join(destdir, name))
2488 if os.path.lexists(dst):
2489 src = os.path.realpath(os.path.join(srcdir, name))
2490 # Fail if the links are pointing to the wrong place
2491 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002492 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002493 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002494 'work tree. If you\'re comfortable with the '
2495 'possibility of losing the work tree\'s git metadata,'
2496 ' use `repo sync --force-sync {0}` to '
2497 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002498
David James8d201162013-10-11 17:03:19 -07002499 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2500 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2501
2502 Args:
2503 gitdir: The bare git repository. Must already be initialized.
2504 dotgit: The repository you would like to initialize.
2505 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2506 Only one work tree can store refs under a given |gitdir|.
2507 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2508 This saves you the effort of initializing |dotgit| yourself.
2509 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002510 symlink_files = self.shareable_files[:]
2511 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002512 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002513 symlink_files += self.working_tree_files
2514 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002515 to_symlink = symlink_files + symlink_dirs
2516
2517 to_copy = []
2518 if copy_all:
2519 to_copy = os.listdir(gitdir)
2520
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002521 dotgit = os.path.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002522 for name in set(to_copy).union(to_symlink):
2523 try:
2524 src = os.path.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002525 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002526
Kevin Degi384b3c52014-10-16 16:02:58 -06002527 if os.path.lexists(dst):
2528 continue
David James8d201162013-10-11 17:03:19 -07002529
2530 # If the source dir doesn't exist, create an empty dir.
2531 if name in symlink_dirs and not os.path.lexists(src):
2532 os.makedirs(src)
2533
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002534 if name in to_symlink:
2535 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
2536 elif copy_all and not os.path.islink(dst):
2537 if os.path.isdir(src):
2538 shutil.copytree(src, dst)
2539 elif os.path.isfile(src):
2540 shutil.copy(src, dst)
2541
Conley Owens80b87fe2014-05-09 17:13:44 -07002542 # If the source file doesn't exist, ensure the destination
2543 # file doesn't either.
2544 if name in symlink_files and not os.path.lexists(src):
2545 try:
2546 os.remove(dst)
2547 except OSError:
2548 pass
2549
David James8d201162013-10-11 17:03:19 -07002550 except OSError as e:
2551 if e.errno == errno.EPERM:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002552 raise DownloadError('filesystem must support symlinks')
David James8d201162013-10-11 17:03:19 -07002553 else:
2554 raise
2555
Kevin Degiabaa7f32014-11-12 11:27:45 -07002556 def _InitWorkTree(self, force_sync=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002557 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002558 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002559 try:
2560 if init_dotgit:
2561 os.makedirs(dotgit)
2562 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2563 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002564
Kevin Degiabaa7f32014-11-12 11:27:45 -07002565 try:
2566 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2567 except GitError as e:
2568 if force_sync:
2569 try:
2570 shutil.rmtree(dotgit)
2571 return self._InitWorkTree(force_sync=False)
2572 except:
2573 raise e
2574 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002575
Kevin Degib1a07b82015-07-27 13:33:43 -06002576 if init_dotgit:
2577 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002578
Kevin Degib1a07b82015-07-27 13:33:43 -06002579 cmd = ['read-tree', '--reset', '-u']
2580 cmd.append('-v')
2581 cmd.append(HEAD)
2582 if GitCommand(self, cmd).Wait() != 0:
2583 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002584
Kevin Degib1a07b82015-07-27 13:33:43 -06002585 self._CopyAndLinkFiles()
2586 except Exception:
2587 if init_dotgit:
2588 shutil.rmtree(dotgit)
2589 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002590
2591 def _gitdir_path(self, path):
David James8d201162013-10-11 17:03:19 -07002592 return os.path.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002593
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002594 def _revlist(self, *args, **kw):
2595 a = []
2596 a.extend(args)
2597 a.append('--')
2598 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002599
2600 @property
2601 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002602 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002603
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002604 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002605 """Get logs between two revisions of this project."""
2606 comp = '..'
2607 if rev1:
2608 revs = [rev1]
2609 if rev2:
2610 revs.extend([comp, rev2])
2611 cmd = ['log', ''.join(revs)]
2612 out = DiffColoring(self.config)
2613 if out.is_on and color:
2614 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002615 if pretty_format is not None:
2616 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002617 if oneline:
2618 cmd.append('--oneline')
2619
2620 try:
2621 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2622 if log.Wait() == 0:
2623 return log.stdout
2624 except GitError:
2625 # worktree may not exist if groups changed for example. In that case,
2626 # try in gitdir instead.
2627 if not os.path.exists(self.worktree):
2628 return self.bare_git.log(*cmd[1:])
2629 else:
2630 raise
2631 return None
2632
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002633 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2634 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002635 """Get the list of logs from this revision to given revisionId"""
2636 logs = {}
2637 selfId = self.GetRevisionId(self._allrefs)
2638 toId = toProject.GetRevisionId(toProject._allrefs)
2639
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002640 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2641 pretty_format=pretty_format)
2642 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2643 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002644 return logs
2645
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002646 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002647
David James8d201162013-10-11 17:03:19 -07002648 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002649 self._project = project
2650 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002651 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002652
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002653 def LsOthers(self):
2654 p = GitCommand(self._project,
2655 ['ls-files',
2656 '-z',
2657 '--others',
2658 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002659 bare=False,
David James8d201162013-10-11 17:03:19 -07002660 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002661 capture_stdout=True,
2662 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002663 if p.Wait() == 0:
2664 out = p.stdout
2665 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002666 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002667 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002668 return []
2669
2670 def DiffZ(self, name, *args):
2671 cmd = [name]
2672 cmd.append('-z')
2673 cmd.extend(args)
2674 p = GitCommand(self._project,
2675 cmd,
David James8d201162013-10-11 17:03:19 -07002676 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002677 bare=False,
2678 capture_stdout=True,
2679 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002680 try:
2681 out = p.process.stdout.read()
2682 r = {}
2683 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002684 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002685 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002686 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002687 info = next(out)
2688 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002689 except StopIteration:
2690 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002691
2692 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002693
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002694 def __init__(self, path, omode, nmode, oid, nid, state):
2695 self.path = path
2696 self.src_path = None
2697 self.old_mode = omode
2698 self.new_mode = nmode
2699 self.old_id = oid
2700 self.new_id = nid
2701
2702 if len(state) == 1:
2703 self.status = state
2704 self.level = None
2705 else:
2706 self.status = state[:1]
2707 self.level = state[1:]
2708 while self.level.startswith('0'):
2709 self.level = self.level[1:]
2710
2711 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002712 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002713 if info.status in ('R', 'C'):
2714 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002715 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002716 r[info.path] = info
2717 return r
2718 finally:
2719 p.Wait()
2720
2721 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002722 if self._bare:
2723 path = os.path.join(self._project.gitdir, HEAD)
2724 else:
2725 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002726 try:
2727 fd = open(path, 'rb')
Dan Sandler53e902a2014-03-09 13:20:02 -04002728 except IOError as e:
2729 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002730 try:
2731 line = fd.read()
2732 finally:
2733 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302734 try:
2735 line = line.decode()
2736 except AttributeError:
2737 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002738 if line.startswith('ref: '):
2739 return line[5:-1]
2740 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741
2742 def SetHead(self, ref, message=None):
2743 cmdv = []
2744 if message is not None:
2745 cmdv.extend(['-m', message])
2746 cmdv.append(HEAD)
2747 cmdv.append(ref)
2748 self.symbolic_ref(*cmdv)
2749
2750 def DetachHead(self, new, message=None):
2751 cmdv = ['--no-deref']
2752 if message is not None:
2753 cmdv.extend(['-m', message])
2754 cmdv.append(HEAD)
2755 cmdv.append(new)
2756 self.update_ref(*cmdv)
2757
2758 def UpdateRef(self, name, new, old=None,
2759 message=None,
2760 detach=False):
2761 cmdv = []
2762 if message is not None:
2763 cmdv.extend(['-m', message])
2764 if detach:
2765 cmdv.append('--no-deref')
2766 cmdv.append(name)
2767 cmdv.append(new)
2768 if old is not None:
2769 cmdv.append(old)
2770 self.update_ref(*cmdv)
2771
2772 def DeleteRef(self, name, old=None):
2773 if not old:
2774 old = self.rev_parse(name)
2775 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002776 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002777
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002778 def rev_list(self, *args, **kw):
2779 if 'format' in kw:
2780 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2781 else:
2782 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002783 cmdv.extend(args)
2784 p = GitCommand(self._project,
2785 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002786 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002787 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002788 capture_stdout=True,
2789 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002790 r = []
2791 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002792 if line[-1] == '\n':
2793 line = line[:-1]
2794 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002795 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002796 raise GitError('%s rev-list %s: %s' %
2797 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002798 return r
2799
2800 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002801 """Allow arbitrary git commands using pythonic syntax.
2802
2803 This allows you to do things like:
2804 git_obj.rev_parse('HEAD')
2805
2806 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2807 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002808 Any other positional arguments will be passed to the git command, and the
2809 following keyword arguments are supported:
2810 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002811
2812 Args:
2813 name: The name of the git command to call. Any '_' characters will
2814 be replaced with '-'.
2815
2816 Returns:
2817 A callable object that will try to call git with the named command.
2818 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002819 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002820
Dave Borowitz091f8932012-10-23 17:01:04 -07002821 def runner(*args, **kwargs):
2822 cmdv = []
2823 config = kwargs.pop('config', None)
2824 for k in kwargs:
2825 raise TypeError('%s() got an unexpected keyword argument %r'
2826 % (name, k))
2827 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002828 if not git_require((1, 7, 2)):
2829 raise ValueError('cannot set config on command line for %s()'
2830 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302831 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002832 cmdv.append('-c')
2833 cmdv.append('%s=%s' % (k, v))
2834 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002835 cmdv.extend(args)
2836 p = GitCommand(self._project,
2837 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002838 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002839 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002840 capture_stdout=True,
2841 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002842 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002843 raise GitError('%s %s: %s' %
2844 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002845 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302846 try:
Conley Owensedd01512013-09-26 12:59:58 -07002847 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302848 except AttributeError:
2849 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002850 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2851 return r[:-1]
2852 return r
2853 return runner
2854
2855
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002856class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002857
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002858 def __str__(self):
2859 return 'prior sync failed; rebase still in progress'
2860
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002861
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002862class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002863
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002864 def __str__(self):
2865 return 'contains uncommitted changes'
2866
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002867
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002868class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002869
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002870 def __init__(self, project, text):
2871 self.project = project
2872 self.text = text
2873
2874 def Print(self, syncbuf):
2875 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2876 syncbuf.out.nl()
2877
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002878
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002879class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002880
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002881 def __init__(self, project, why):
2882 self.project = project
2883 self.why = why
2884
2885 def Print(self, syncbuf):
2886 syncbuf.out.fail('error: %s/: %s',
2887 self.project.relpath,
2888 str(self.why))
2889 syncbuf.out.nl()
2890
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002891
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002892class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002893
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002894 def __init__(self, project, action):
2895 self.project = project
2896 self.action = action
2897
2898 def Run(self, syncbuf):
2899 out = syncbuf.out
2900 out.project('project %s/', self.project.relpath)
2901 out.nl()
2902 try:
2903 self.action()
2904 out.nl()
2905 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002906 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002907 out.nl()
2908 return False
2909
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002910
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002911class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002912
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002913 def __init__(self, config):
2914 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002915 self.project = self.printer('header', attr='bold')
2916 self.info = self.printer('info')
2917 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002918
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002919
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002920class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002921
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002922 def __init__(self, config, detach_head=False):
2923 self._messages = []
2924 self._failures = []
2925 self._later_queue1 = []
2926 self._later_queue2 = []
2927
2928 self.out = _SyncColoring(config)
2929 self.out.redirect(sys.stderr)
2930
2931 self.detach_head = detach_head
2932 self.clean = True
2933
2934 def info(self, project, fmt, *args):
2935 self._messages.append(_InfoMessage(project, fmt % args))
2936
2937 def fail(self, project, err=None):
2938 self._failures.append(_Failure(project, err))
2939 self.clean = False
2940
2941 def later1(self, project, what):
2942 self._later_queue1.append(_Later(project, what))
2943
2944 def later2(self, project, what):
2945 self._later_queue2.append(_Later(project, what))
2946
2947 def Finish(self):
2948 self._PrintMessages()
2949 self._RunLater()
2950 self._PrintMessages()
2951 return self.clean
2952
2953 def _RunLater(self):
2954 for q in ['_later_queue1', '_later_queue2']:
2955 if not self._RunQueue(q):
2956 return
2957
2958 def _RunQueue(self, queue):
2959 for m in getattr(self, queue):
2960 if not m.Run(self):
2961 self.clean = False
2962 return False
2963 setattr(self, queue, [])
2964 return True
2965
2966 def _PrintMessages(self):
2967 for m in self._messages:
2968 m.Print(self)
2969 for m in self._failures:
2970 m.Print(self)
2971
2972 self._messages = []
2973 self._failures = []
2974
2975
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002976class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002977
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002978 """A special project housed under .repo.
2979 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002980
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002981 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002982 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002983 manifest=manifest,
2984 name=name,
2985 gitdir=gitdir,
2986 objdir=gitdir,
2987 worktree=worktree,
2988 remote=RemoteSpec('origin'),
2989 relpath='.repo/%s' % name,
2990 revisionExpr='refs/heads/master',
2991 revisionId=None,
2992 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002993
2994 def PreSync(self):
2995 if self.Exists:
2996 cb = self.CurrentBranch
2997 if cb:
2998 base = self.GetBranch(cb).merge
2999 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003000 self.revisionExpr = base
3001 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003002
Anthony King7bdac712014-07-16 12:56:40 +01003003 def MetaBranchSwitch(self):
Florian Vallee5d016502012-06-07 17:19:26 +02003004 """ Prepare MetaProject for manifest branch switch
3005 """
3006
3007 # detach and delete manifest branch, allowing a new
3008 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01003009 syncbuf = SyncBuffer(self.config, detach_head=True)
Florian Vallee5d016502012-06-07 17:19:26 +02003010 self.Sync_LocalHalf(syncbuf)
3011 syncbuf.Finish()
3012
3013 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07003014 ['update-ref', '-d', 'refs/heads/default'],
3015 capture_stdout=True,
3016 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02003017
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003018 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07003019 def LastFetch(self):
3020 try:
3021 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
3022 return os.path.getmtime(fh)
3023 except OSError:
3024 return 0
3025
3026 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003027 def HasChanges(self):
3028 """Has the remote received new commits not yet checked out?
3029 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003030 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003031 return False
3032
David Pursehouse8a68ff92012-09-24 12:15:13 +09003033 all_refs = self.bare_ref.all
3034 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003035 head = self.work_git.GetHead()
3036 if head.startswith(R_HEADS):
3037 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09003038 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003039 except KeyError:
3040 head = None
3041
3042 if revid == head:
3043 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003044 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003045 return True
3046 return False