blob: 5e2095106745e98a65edd6f4f64f719da66a1def [file] [log] [blame]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Sarah Owenscecd1d82012-11-01 22:59:27 -070015from __future__ import print_function
Shawn O. Pearce438ee1c2008-11-03 09:59:36 -080016import errno
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070017import filecmp
Wink Saville4c426ef2015-06-03 08:05:17 -070018import glob
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070019import os
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070020import random
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070021import re
22import shutil
23import stat
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070024import subprocess
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070025import sys
Julien Campergue335f5ef2013-10-16 11:02:35 +020026import tarfile
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +080027import tempfile
Shawn O. Pearcec325dc32011-10-03 08:30:24 -070028import time
Dave Borowitz137d0132015-01-02 11:12:54 -080029import traceback
Shawn O. Pearcedf5ee522011-10-11 14:05:21 -070030
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070031from color import Coloring
Dave Borowitzb42b4742012-10-31 12:27:27 -070032from git_command import GitCommand, git_require
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070033from git_config import GitConfig, IsId, GetSchemeFromUrl, GetUrlCookieFile, \
34 ID_RE
Kevin Degiabaa7f32014-11-12 11:27:45 -070035from error import GitError, HookError, UploadError, DownloadError
Shawn O. Pearce559b8462009-03-02 12:56:08 -080036from error import ManifestInvalidRevisionError
Conley Owens75ee0572012-11-15 17:33:11 -080037from error import NoManifestException
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -070038from trace import IsTrace, Trace
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070039
Shawn O. Pearced237b692009-04-17 18:49:50 -070040from git_refs import GitRefs, HEAD, R_HEADS, R_TAGS, R_PUB, R_M
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070041
David Pursehouse59bbb582013-05-17 10:49:33 +090042from pyversion import is_python3
Mike Frysinger40252c22016-08-15 21:23:44 -040043if is_python3():
44 import urllib.parse
45else:
46 import imp
47 import urlparse
48 urllib = imp.new_module('urllib')
49 urllib.parse = urlparse
David Pursehouse59bbb582013-05-17 10:49:33 +090050 # pylint:disable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053051 input = raw_input
David Pursehouse59bbb582013-05-17 10:49:33 +090052 # pylint:enable=W0622
Chirayu Desai217ea7d2013-03-01 19:14:38 +053053
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070054
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070055def _lwrite(path, content):
56 lock = '%s.lock' % path
57
Chirayu Desai303a82f2014-08-19 22:57:17 +053058 fd = open(lock, 'w')
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -070059 try:
60 fd.write(content)
61 finally:
62 fd.close()
63
64 try:
65 os.rename(lock, path)
66 except OSError:
67 os.remove(lock)
68 raise
69
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070070
Shawn O. Pearce48244782009-04-16 08:25:57 -070071def _error(fmt, *args):
72 msg = fmt % args
Sarah Owenscecd1d82012-11-01 22:59:27 -070073 print('error: %s' % msg, file=sys.stderr)
Shawn O. Pearce48244782009-04-16 08:25:57 -070074
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070075
David Pursehousef33929d2015-08-24 14:39:14 +090076def _warn(fmt, *args):
77 msg = fmt % args
78 print('warn: %s' % msg, file=sys.stderr)
79
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070080
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -070081def not_rev(r):
82 return '^' + r
83
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070084
Shawn O. Pearceb54a3922009-01-05 16:18:58 -080085def sq(r):
86 return "'" + r.replace("'", "'\''") + "'"
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -080087
Jonathan Nieder93719792015-03-17 11:29:58 -070088_project_hook_list = None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -070089
90
Jonathan Nieder93719792015-03-17 11:29:58 -070091def _ProjectHooks():
92 """List the hooks present in the 'hooks' directory.
93
94 These hooks are project hooks and are copied to the '.git/hooks' directory
95 of all subprojects.
96
97 This function caches the list of hooks (based on the contents of the
98 'repo/hooks' directory) on the first call.
99
100 Returns:
101 A list of absolute paths to all of the files in the hooks directory.
102 """
103 global _project_hook_list
104 if _project_hook_list is None:
105 d = os.path.realpath(os.path.abspath(os.path.dirname(__file__)))
106 d = os.path.join(d, 'hooks')
107 _project_hook_list = [os.path.join(d, x) for x in os.listdir(d)]
108 return _project_hook_list
109
110
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700111class DownloadedChange(object):
112 _commit_cache = None
113
114 def __init__(self, project, base, change_id, ps_id, commit):
115 self.project = project
116 self.base = base
117 self.change_id = change_id
118 self.ps_id = ps_id
119 self.commit = commit
120
121 @property
122 def commits(self):
123 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700124 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
125 '--abbrev-commit',
126 '--pretty=oneline',
127 '--reverse',
128 '--date-order',
129 not_rev(self.base),
130 self.commit,
131 '--')
Shawn O. Pearce632768b2008-10-23 11:58:52 -0700132 return self._commit_cache
133
134
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700135class ReviewableBranch(object):
136 _commit_cache = None
137
138 def __init__(self, project, branch, base):
139 self.project = project
140 self.branch = branch
141 self.base = base
142
143 @property
144 def name(self):
145 return self.branch.name
146
147 @property
148 def commits(self):
149 if self._commit_cache is None:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700150 self._commit_cache = self.project.bare_git.rev_list('--abbrev=8',
151 '--abbrev-commit',
152 '--pretty=oneline',
153 '--reverse',
154 '--date-order',
155 not_rev(self.base),
156 R_HEADS + self.name,
157 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700158 return self._commit_cache
159
160 @property
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800161 def unabbrev_commits(self):
162 r = dict()
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700163 for commit in self.project.bare_git.rev_list(not_rev(self.base),
164 R_HEADS + self.name,
165 '--'):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800166 r[commit[0:8]] = commit
167 return r
168
169 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700170 def date(self):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700171 return self.project.bare_git.log('--pretty=format:%cd',
172 '-n', '1',
173 R_HEADS + self.name,
174 '--')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700175
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700176 def UploadForReview(self, people,
177 auto_topic=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200178 private=False,
179 wip=False,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700180 dest_branch=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800181 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700182 people,
Brian Harring435370c2012-07-28 15:37:04 -0700183 auto_topic=auto_topic,
Changcheng Xiao87984c62017-08-02 16:55:03 +0200184 private=private,
185 wip=wip,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400186 dest_branch=dest_branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700187
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700188 def GetPublishedRefs(self):
189 refs = {}
190 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700191 self.branch.remote.SshReviewUrl(self.project.UserEmail),
192 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700193 for line in output.split('\n'):
194 try:
195 (sha, ref) = line.split()
196 refs[sha] = ref
197 except ValueError:
198 pass
199
200 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700201
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700202
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700203class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700204
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700205 def __init__(self, config):
206 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100207 self.project = self.printer('header', attr='bold')
208 self.branch = self.printer('header', attr='bold')
209 self.nobranch = self.printer('nobranch', fg='red')
210 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700211
Anthony King7bdac712014-07-16 12:56:40 +0100212 self.added = self.printer('added', fg='green')
213 self.changed = self.printer('changed', fg='red')
214 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700215
216
217class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700218
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700219 def __init__(self, config):
220 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100221 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700222
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700223
Anthony King7bdac712014-07-16 12:56:40 +0100224class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700225
James W. Mills24c13082012-04-12 15:04:13 -0500226 def __init__(self, name, value, keep):
227 self.name = name
228 self.value = value
229 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700230
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
Anthony King7bdac712014-07-16 12:56:40 +0100232class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700233
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800234 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700235 self.src = src
236 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800237 self.abs_src = abssrc
238 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700239
240 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800241 src = self.abs_src
242 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700243 # copy file if it does not exist or is out of date
244 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
245 try:
246 # remove existing file first, since it might be read-only
247 if os.path.exists(dest):
248 os.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400249 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200250 dest_dir = os.path.dirname(dest)
251 if not os.path.isdir(dest_dir):
252 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700253 shutil.copy(src, dest)
254 # make the file read-only
255 mode = os.stat(dest)[stat.ST_MODE]
256 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
257 os.chmod(dest, mode)
258 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700259 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700260
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700261
Anthony King7bdac712014-07-16 12:56:40 +0100262class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700263
Wink Saville4c426ef2015-06-03 08:05:17 -0700264 def __init__(self, git_worktree, src, dest, relsrc, absdest):
265 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500266 self.src = src
267 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700268 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500269 self.abs_dest = absdest
270
Wink Saville4c426ef2015-06-03 08:05:17 -0700271 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500272 # link file if it does not exist or is out of date
Wink Saville4c426ef2015-06-03 08:05:17 -0700273 if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500274 try:
275 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800276 if os.path.lexists(absDest):
Wink Saville4c426ef2015-06-03 08:05:17 -0700277 os.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500278 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700279 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500280 if not os.path.isdir(dest_dir):
281 os.makedirs(dest_dir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 os.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500283 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700284 _error('Cannot link file %s to %s', relSrc, absDest)
285
286 def _Link(self):
287 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
288 on the src linking all of the files in the source in to the destination
289 directory.
290 """
291 # We use the absSrc to handle the situation where the current directory
292 # is not the root of the repo
293 absSrc = os.path.join(self.git_worktree, self.src)
294 if os.path.exists(absSrc):
295 # Entity exists so just a simple one to one link operation
296 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
297 else:
298 # Entity doesn't exist assume there is a wild card
299 absDestDir = self.abs_dest
300 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
301 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700302 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700303 else:
304 absSrcFiles = glob.glob(absSrc)
305 for absSrcFile in absSrcFiles:
306 # Create a releative path from source dir to destination dir
307 absSrcDir = os.path.dirname(absSrcFile)
308 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
309
310 # Get the source file name
311 srcFile = os.path.basename(absSrcFile)
312
313 # Now form the final full paths to srcFile. They will be
314 # absolute for the desintaiton and relative for the srouce.
315 absDest = os.path.join(absDestDir, srcFile)
316 relSrc = os.path.join(relSrcDir, srcFile)
317 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500318
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700319
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700320class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700321
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700322 def __init__(self,
323 name,
Anthony King7bdac712014-07-16 12:56:40 +0100324 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700325 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100326 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700327 revision=None,
David Rileye0684ad2017-04-05 00:02:59 -0700328 orig_name=None,
329 fetchUrl=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330 self.name = name
331 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700332 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700333 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100334 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700335 self.orig_name = orig_name
David Rileye0684ad2017-04-05 00:02:59 -0700336 self.fetchUrl = fetchUrl
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700337
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700338
Doug Anderson37282b42011-03-04 11:54:18 -0800339class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700340
Doug Anderson37282b42011-03-04 11:54:18 -0800341 """A RepoHook contains information about a script to run as a hook.
342
343 Hooks are used to run a python script before running an upload (for instance,
344 to run presubmit checks). Eventually, we may have hooks for other actions.
345
346 This shouldn't be confused with files in the 'repo/hooks' directory. Those
347 files are copied into each '.git/hooks' folder for each project. Repo-level
348 hooks are associated instead with repo actions.
349
350 Hooks are always python. When a hook is run, we will load the hook into the
351 interpreter and execute its main() function.
352 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700353
Doug Anderson37282b42011-03-04 11:54:18 -0800354 def __init__(self,
355 hook_type,
356 hooks_project,
357 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400358 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800359 abort_if_user_denies=False):
360 """RepoHook constructor.
361
362 Params:
363 hook_type: A string representing the type of hook. This is also used
364 to figure out the name of the file containing the hook. For
365 example: 'pre-upload'.
366 hooks_project: The project containing the repo hooks. If you have a
367 manifest, this is manifest.repo_hooks_project. OK if this is None,
368 which will make the hook a no-op.
369 topdir: Repo's top directory (the one containing the .repo directory).
370 Scripts will run with CWD as this directory. If you have a manifest,
371 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400372 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800373 abort_if_user_denies: If True, we'll throw a HookError() if the user
374 doesn't allow us to run the hook.
375 """
376 self._hook_type = hook_type
377 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400378 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800379 self._topdir = topdir
380 self._abort_if_user_denies = abort_if_user_denies
381
382 # Store the full path to the script for convenience.
383 if self._hooks_project:
384 self._script_fullpath = os.path.join(self._hooks_project.worktree,
385 self._hook_type + '.py')
386 else:
387 self._script_fullpath = None
388
389 def _GetHash(self):
390 """Return a hash of the contents of the hooks directory.
391
392 We'll just use git to do this. This hash has the property that if anything
393 changes in the directory we will return a different has.
394
395 SECURITY CONSIDERATION:
396 This hash only represents the contents of files in the hook directory, not
397 any other files imported or called by hooks. Changes to imported files
398 can change the script behavior without affecting the hash.
399
400 Returns:
401 A string representing the hash. This will always be ASCII so that it can
402 be printed to the user easily.
403 """
404 assert self._hooks_project, "Must have hooks to calculate their hash."
405
406 # We will use the work_git object rather than just calling GetRevisionId().
407 # That gives us a hash of the latest checked in version of the files that
408 # the user will actually be executing. Specifically, GetRevisionId()
409 # doesn't appear to change even if a user checks out a different version
410 # of the hooks repo (via git checkout) nor if a user commits their own revs.
411 #
412 # NOTE: Local (non-committed) changes will not be factored into this hash.
413 # I think this is OK, since we're really only worried about warning the user
414 # about upstream changes.
415 return self._hooks_project.work_git.rev_parse('HEAD')
416
417 def _GetMustVerb(self):
418 """Return 'must' if the hook is required; 'should' if not."""
419 if self._abort_if_user_denies:
420 return 'must'
421 else:
422 return 'should'
423
424 def _CheckForHookApproval(self):
425 """Check to see whether this hook has been approved.
426
Mike Frysinger40252c22016-08-15 21:23:44 -0400427 We'll accept approval of manifest URLs if they're using secure transports.
428 This way the user can say they trust the manifest hoster. For insecure
429 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800430
431 Note that we ask permission for each individual hook even though we use
432 the hash of all hooks when detecting changes. We'd like the user to be
433 able to approve / deny each hook individually. We only use the hash of all
434 hooks because there is no other easy way to detect changes to local imports.
435
436 Returns:
437 True if this hook is approved to run; False otherwise.
438
439 Raises:
440 HookError: Raised if the user doesn't approve and abort_if_user_denies
441 was passed to the consturctor.
442 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400443 if self._ManifestUrlHasSecureScheme():
444 return self._CheckForHookApprovalManifest()
445 else:
446 return self._CheckForHookApprovalHash()
447
448 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
449 changed_prompt):
450 """Check for approval for a particular attribute and hook.
451
452 Args:
453 subkey: The git config key under [repo.hooks.<hook_type>] to store the
454 last approved string.
455 new_val: The new value to compare against the last approved one.
456 main_prompt: Message to display to the user to ask for approval.
457 changed_prompt: Message explaining why we're re-asking for approval.
458
459 Returns:
460 True if this hook is approved to run; False otherwise.
461
462 Raises:
463 HookError: Raised if the user doesn't approve and abort_if_user_denies
464 was passed to the consturctor.
465 """
Doug Anderson37282b42011-03-04 11:54:18 -0800466 hooks_config = self._hooks_project.config
Mike Frysinger40252c22016-08-15 21:23:44 -0400467 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800468
Mike Frysinger40252c22016-08-15 21:23:44 -0400469 # Get the last value that the user approved for this hook; may be None.
470 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800471
Mike Frysinger40252c22016-08-15 21:23:44 -0400472 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800473 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400474 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800475 # Approval matched. We're done.
476 return True
477 else:
478 # Give the user a reason why we're prompting, since they last told
479 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400480 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800481 else:
482 prompt = ''
483
484 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
485 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400486 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530487 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900488 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800489
490 # User is doing a one-time approval.
491 if response in ('y', 'yes'):
492 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400493 elif response == 'always':
494 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800495 return True
496
497 # For anything else, we'll assume no approval.
498 if self._abort_if_user_denies:
499 raise HookError('You must allow the %s hook or use --no-verify.' %
500 self._hook_type)
501
502 return False
503
Mike Frysinger40252c22016-08-15 21:23:44 -0400504 def _ManifestUrlHasSecureScheme(self):
505 """Check if the URI for the manifest is a secure transport."""
506 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
507 parse_results = urllib.parse.urlparse(self._manifest_url)
508 return parse_results.scheme in secure_schemes
509
510 def _CheckForHookApprovalManifest(self):
511 """Check whether the user has approved this manifest host.
512
513 Returns:
514 True if this hook is approved to run; False otherwise.
515 """
516 return self._CheckForHookApprovalHelper(
517 'approvedmanifest',
518 self._manifest_url,
519 'Run hook scripts from %s' % (self._manifest_url,),
520 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
521
522 def _CheckForHookApprovalHash(self):
523 """Check whether the user has approved the hooks repo.
524
525 Returns:
526 True if this hook is approved to run; False otherwise.
527 """
528 prompt = ('Repo %s run the script:\n'
529 ' %s\n'
530 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700531 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400532 return self._CheckForHookApprovalHelper(
533 'approvedhash',
534 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700535 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400536 'Scripts have changed since %s was allowed.' % (self._hook_type,))
537
Doug Anderson37282b42011-03-04 11:54:18 -0800538 def _ExecuteHook(self, **kwargs):
539 """Actually execute the given hook.
540
541 This will run the hook's 'main' function in our python interpreter.
542
543 Args:
544 kwargs: Keyword arguments to pass to the hook. These are often specific
545 to the hook type. For instance, pre-upload hooks will contain
546 a project_list.
547 """
548 # Keep sys.path and CWD stashed away so that we can always restore them
549 # upon function exit.
550 orig_path = os.getcwd()
551 orig_syspath = sys.path
552
553 try:
554 # Always run hooks with CWD as topdir.
555 os.chdir(self._topdir)
556
557 # Put the hook dir as the first item of sys.path so hooks can do
558 # relative imports. We want to replace the repo dir as [0] so
559 # hooks can't import repo files.
560 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
561
562 # Exec, storing global context in the context dict. We catch exceptions
563 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500564 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800565 try:
Anthony King70f68902014-05-05 21:15:34 +0100566 exec(compile(open(self._script_fullpath).read(),
567 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800568 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700569 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
570 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800571
572 # Running the script should have defined a main() function.
573 if 'main' not in context:
574 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
575
Doug Anderson37282b42011-03-04 11:54:18 -0800576 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
577 # We don't actually want hooks to define their main with this argument--
578 # it's there to remind them that their hook should always take **kwargs.
579 # For instance, a pre-upload hook should be defined like:
580 # def main(project_list, **kwargs):
581 #
582 # This allows us to later expand the API without breaking old hooks.
583 kwargs = kwargs.copy()
584 kwargs['hook_should_take_kwargs'] = True
585
586 # Call the main function in the hook. If the hook should cause the
587 # build to fail, it will raise an Exception. We'll catch that convert
588 # to a HookError w/ just the failing traceback.
589 try:
590 context['main'](**kwargs)
591 except Exception:
592 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700593 'above.' % (traceback.format_exc(),
594 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800595 finally:
596 # Restore sys.path and CWD.
597 sys.path = orig_syspath
598 os.chdir(orig_path)
599
600 def Run(self, user_allows_all_hooks, **kwargs):
601 """Run the hook.
602
603 If the hook doesn't exist (because there is no hooks project or because
604 this particular hook is not enabled), this is a no-op.
605
606 Args:
607 user_allows_all_hooks: If True, we will never prompt about running the
608 hook--we'll just assume it's OK to run it.
609 kwargs: Keyword arguments to pass to the hook. These are often specific
610 to the hook type. For instance, pre-upload hooks will contain
611 a project_list.
612
613 Raises:
614 HookError: If there was a problem finding the hook or the user declined
615 to run a required hook (from _CheckForHookApproval).
616 """
617 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700618 if ((not self._hooks_project) or (self._hook_type not in
619 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800620 return
621
622 # Bail with a nice error if we can't find the hook.
623 if not os.path.isfile(self._script_fullpath):
624 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
625
626 # Make sure the user is OK with running the hook.
627 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
628 return
629
630 # Run the hook with the same version of python we're using.
631 self._ExecuteHook(**kwargs)
632
633
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700634class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600635 # These objects can be shared between several working trees.
636 shareable_files = ['description', 'info']
637 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
638 # These objects can only be used by a single working tree.
639 working_tree_files = ['config', 'packed-refs', 'shallow']
640 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700641
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700642 def __init__(self,
643 manifest,
644 name,
645 remote,
646 gitdir,
David James8d201162013-10-11 17:03:19 -0700647 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700648 worktree,
649 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700650 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800651 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100652 rebase=True,
653 groups=None,
654 sync_c=False,
655 sync_s=False,
656 clone_depth=None,
657 upstream=None,
658 parent=None,
659 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900660 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700661 optimized_fetch=False,
662 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800663 """Init a Project object.
664
665 Args:
666 manifest: The XmlManifest object.
667 name: The `name` attribute of manifest.xml's project element.
668 remote: RemoteSpec object specifying its remote's properties.
669 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700670 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800671 worktree: Absolute path of git working tree.
672 relpath: Relative path of git working tree to repo's top directory.
673 revisionExpr: The `revision` attribute of manifest.xml's project element.
674 revisionId: git commit id for checking out.
675 rebase: The `rebase` attribute of manifest.xml's project element.
676 groups: The `groups` attribute of manifest.xml's project element.
677 sync_c: The `sync-c` attribute of manifest.xml's project element.
678 sync_s: The `sync-s` attribute of manifest.xml's project element.
679 upstream: The `upstream` attribute of manifest.xml's project element.
680 parent: The parent Project object.
681 is_derived: False if the project was explicitly defined in the manifest;
682 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400683 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900684 optimized_fetch: If True, when a project is set to a sha1 revision, only
685 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700686 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800687 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700688 self.manifest = manifest
689 self.name = name
690 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800691 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700692 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800693 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700694 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800695 else:
696 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700697 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700698 self.revisionExpr = revisionExpr
699
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700700 if revisionId is None \
701 and revisionExpr \
702 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700703 self.revisionId = revisionExpr
704 else:
705 self.revisionId = revisionId
706
Mike Pontillod3153822012-02-28 11:53:24 -0800707 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700708 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700709 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800710 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900711 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700712 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800713 self.parent = parent
714 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900715 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800716 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800717
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700718 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700719 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500720 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500721 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700722 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
723 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700724
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800725 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700726 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800727 else:
728 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700729 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700730 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700731 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400732 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700733 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700734
Doug Anderson37282b42011-03-04 11:54:18 -0800735 # This will be filled in if a project is later identified to be the
736 # project containing repo hooks.
737 self.enabled_repo_hooks = []
738
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700739 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800740 def Derived(self):
741 return self.is_derived
742
743 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700744 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600745 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700746
747 @property
748 def CurrentBranch(self):
749 """Obtain the name of the currently checked out branch.
750 The branch name omits the 'refs/heads/' prefix.
751 None is returned if the project is on a detached HEAD.
752 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700753 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700754 if b.startswith(R_HEADS):
755 return b[len(R_HEADS):]
756 return None
757
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700758 def IsRebaseInProgress(self):
759 w = self.worktree
760 g = os.path.join(w, '.git')
761 return os.path.exists(os.path.join(g, 'rebase-apply')) \
762 or os.path.exists(os.path.join(g, 'rebase-merge')) \
763 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200764
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700765 def IsDirty(self, consider_untracked=True):
766 """Is the working directory modified in some way?
767 """
768 self.work_git.update_index('-q',
769 '--unmerged',
770 '--ignore-missing',
771 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900772 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700773 return True
774 if self.work_git.DiffZ('diff-files'):
775 return True
776 if consider_untracked and self.work_git.LsOthers():
777 return True
778 return False
779
780 _userident_name = None
781 _userident_email = None
782
783 @property
784 def UserName(self):
785 """Obtain the user's personal name.
786 """
787 if self._userident_name is None:
788 self._LoadUserIdentity()
789 return self._userident_name
790
791 @property
792 def UserEmail(self):
793 """Obtain the user's email address. This is very likely
794 to be their Gerrit login.
795 """
796 if self._userident_email is None:
797 self._LoadUserIdentity()
798 return self._userident_email
799
800 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900801 u = self.bare_git.var('GIT_COMMITTER_IDENT')
802 m = re.compile("^(.*) <([^>]*)> ").match(u)
803 if m:
804 self._userident_name = m.group(1)
805 self._userident_email = m.group(2)
806 else:
807 self._userident_name = ''
808 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700809
810 def GetRemote(self, name):
811 """Get the configuration for a single remote.
812 """
813 return self.config.GetRemote(name)
814
815 def GetBranch(self, name):
816 """Get the configuration for a single branch.
817 """
818 return self.config.GetBranch(name)
819
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700820 def GetBranches(self):
821 """Get all existing local branches.
822 """
823 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900824 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700825 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700826
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530827 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700828 if name.startswith(R_HEADS):
829 name = name[len(R_HEADS):]
830 b = self.GetBranch(name)
831 b.current = name == current
832 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900833 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700834 heads[name] = b
835
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530836 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700837 if name.startswith(R_PUB):
838 name = name[len(R_PUB):]
839 b = heads.get(name)
840 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900841 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700842
843 return heads
844
Colin Cross5acde752012-03-28 20:15:45 -0700845 def MatchesGroups(self, manifest_groups):
846 """Returns true if the manifest groups specified at init should cause
847 this project to be synced.
848 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700849 All projects are implicitly labelled with "all".
Conley Owens971de8e2012-04-16 10:36:08 -0700850
851 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700852 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700853 manifest_groups: "-group1,group2"
854 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500855
856 The special manifest group "default" will match any project that
857 does not have the special project group "notdefault"
Colin Cross5acde752012-03-28 20:15:45 -0700858 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500859 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700860 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700861 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500862 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700863
Conley Owens971de8e2012-04-16 10:36:08 -0700864 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700865 for group in expanded_manifest_groups:
866 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700867 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700868 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700869 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700870
Conley Owens971de8e2012-04-16 10:36:08 -0700871 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700873# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700874 def UncommitedFiles(self, get_all=True):
875 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700876
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700877 Args:
878 get_all: a boolean, if True - get information about all different
879 uncommitted files. If False - return as soon as any kind of
880 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500881 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700882 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500883 self.work_git.update_index('-q',
884 '--unmerged',
885 '--ignore-missing',
886 '--refresh')
887 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700888 details.append("rebase in progress")
889 if not get_all:
890 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500891
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700892 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
893 if changes:
894 details.extend(changes)
895 if not get_all:
896 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500897
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700898 changes = self.work_git.DiffZ('diff-files').keys()
899 if changes:
900 details.extend(changes)
901 if not get_all:
902 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500903
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700904 changes = self.work_git.LsOthers()
905 if changes:
906 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500907
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700908 return details
909
910 def HasChanges(self):
911 """Returns true if there are uncommitted changes.
912 """
913 if self.UncommitedFiles(get_all=False):
914 return True
915 else:
916 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500917
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600918 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700919 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200920
921 Args:
922 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600923 quiet: If True then only print the project name. Do not print
924 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700925 """
926 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700927 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200928 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700929 print(file=output_redir)
930 print('project %s/' % self.relpath, file=output_redir)
931 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700932 return
933
934 self.work_git.update_index('-q',
935 '--unmerged',
936 '--ignore-missing',
937 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700938 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700939 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
940 df = self.work_git.DiffZ('diff-files')
941 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100942 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700943 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700944
945 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700946 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200947 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700948 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700949
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600950 if quiet:
951 out.nl()
952 return 'DIRTY'
953
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700954 branch = self.CurrentBranch
955 if branch is None:
956 out.nobranch('(*** NO BRANCH ***)')
957 else:
958 out.branch('branch %s', branch)
959 out.nl()
960
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700961 if rb:
962 out.important('prior sync failed; rebase still in progress')
963 out.nl()
964
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700965 paths = list()
966 paths.extend(di.keys())
967 paths.extend(df.keys())
968 paths.extend(do)
969
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530970 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900971 try:
972 i = di[p]
973 except KeyError:
974 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700975
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900976 try:
977 f = df[p]
978 except KeyError:
979 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200980
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900981 if i:
982 i_status = i.status.upper()
983 else:
984 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700985
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900986 if f:
987 f_status = f.status.lower()
988 else:
989 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700990
991 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800992 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700993 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700994 else:
995 line = ' %s%s\t%s' % (i_status, f_status, p)
996
997 if i and not f:
998 out.added('%s', line)
999 elif (i and f) or (not i and f):
1000 out.changed('%s', line)
1001 elif not i and not f:
1002 out.untracked('%s', line)
1003 else:
1004 out.write('%s', line)
1005 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001006
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001007 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001008
pelyad67872d2012-03-28 14:49:58 +03001009 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001010 """Prints the status of the repository to stdout.
1011 """
1012 out = DiffColoring(self.config)
1013 cmd = ['diff']
1014 if out.is_on:
1015 cmd.append('--color')
1016 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001017 if absolute_paths:
1018 cmd.append('--src-prefix=a/%s/' % self.relpath)
1019 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001020 cmd.append('--')
1021 p = GitCommand(self,
1022 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001023 capture_stdout=True,
1024 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001025 has_diff = False
1026 for line in p.process.stdout:
1027 if not has_diff:
1028 out.nl()
1029 out.project('project %s/' % self.relpath)
1030 out.nl()
1031 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001032 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033 p.Wait()
1034
1035
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001036# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001037
David Pursehouse8a68ff92012-09-24 12:15:13 +09001038 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001039 """Was the branch published (uploaded) for code review?
1040 If so, returns the SHA-1 hash of the last published
1041 state for the branch.
1042 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001043 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001044 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001045 try:
1046 return self.bare_git.rev_parse(key)
1047 except GitError:
1048 return None
1049 else:
1050 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001051 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001052 except KeyError:
1053 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001054
David Pursehouse8a68ff92012-09-24 12:15:13 +09001055 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056 """Prunes any stale published refs.
1057 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001058 if all_refs is None:
1059 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001060 heads = set()
1061 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301062 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063 if name.startswith(R_HEADS):
1064 heads.add(name)
1065 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001066 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001067
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301068 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001069 n = name[len(R_PUB):]
1070 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001071 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001072
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001073 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001074 """List any branches which can be uploaded for review.
1075 """
1076 heads = {}
1077 pubed = {}
1078
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301079 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001081 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001082 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001083 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084
1085 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301086 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001087 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001088 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001089 if selected_branch and branch != selected_branch:
1090 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001092 rb = self.GetUploadableBranch(branch)
1093 if rb:
1094 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001095 return ready
1096
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001097 def GetUploadableBranch(self, branch_name):
1098 """Get a single uploadable branch, or None.
1099 """
1100 branch = self.GetBranch(branch_name)
1101 base = branch.LocalMerge
1102 if branch.LocalMerge:
1103 rb = ReviewableBranch(self, branch, base)
1104 if rb.commits:
1105 return rb
1106 return None
1107
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001108 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001109 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001110 auto_topic=False,
Changcheng Xiao87984c62017-08-02 16:55:03 +02001111 private=False,
1112 wip=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001113 dest_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001114 """Uploads the named branch for code review.
1115 """
1116 if branch is None:
1117 branch = self.CurrentBranch
1118 if branch is None:
1119 raise GitError('not currently on a branch')
1120
1121 branch = self.GetBranch(branch)
1122 if not branch.LocalMerge:
1123 raise GitError('branch %s does not track a remote' % branch.name)
1124 if not branch.remote.review:
1125 raise GitError('remote %s has no review url' % branch.remote.name)
1126
Bryan Jacobsf609f912013-05-06 13:36:24 -04001127 if dest_branch is None:
1128 dest_branch = self.dest_branch
1129 if dest_branch is None:
1130 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001131 if not dest_branch.startswith(R_HEADS):
1132 dest_branch = R_HEADS + dest_branch
1133
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001134 if not branch.remote.projectname:
1135 branch.remote.projectname = self.name
1136 branch.remote.Save()
1137
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001138 url = branch.remote.ReviewUrl(self.UserEmail)
1139 if url is None:
1140 raise UploadError('review not configured')
1141 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001142
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001143 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001144 rp = ['gerrit receive-pack']
1145 for e in people[0]:
1146 rp.append('--reviewer=%s' % sq(e))
1147 for e in people[1]:
1148 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001149 cmd.append('--receive-pack=%s' % " ".join(rp))
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
Changcheng Xiaod88f53e2017-08-07 15:05:46 +02001156 ref_spec = '%s:refs/for/%s' % (R_HEADS + branch.name, dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001157 if auto_topic:
1158 ref_spec = ref_spec + '/' + branch.name
Changcheng Xiao87984c62017-08-02 16:55:03 +02001159
Shawn Pearce45d21682013-02-28 00:35:51 -08001160 if not url.startswith('ssh://'):
1161 rp = ['r=%s' % p for p in people[0]] + \
1162 ['cc=%s' % p for p in people[1]]
Changcheng Xiao87984c62017-08-02 16:55:03 +02001163 if private:
1164 rp = rp + ['private']
1165 if wip:
1166 rp = rp + ['wip']
Shawn Pearce45d21682013-02-28 00:35:51 -08001167 if rp:
1168 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001169 cmd.append(ref_spec)
1170
Anthony King7bdac712014-07-16 12:56:40 +01001171 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001172 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001173
1174 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1175 self.bare_git.UpdateRef(R_PUB + branch.name,
1176 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001177 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001178
1179
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001180# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001181
Julien Campergue335f5ef2013-10-16 11:02:35 +02001182 def _ExtractArchive(self, tarpath, path=None):
1183 """Extract the given tar on its current location
1184
1185 Args:
1186 - tarpath: The path to the actual tar file
1187
1188 """
1189 try:
1190 with tarfile.open(tarpath, 'r') as tar:
1191 tar.extractall(path=path)
1192 return True
1193 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001194 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001195 return False
1196
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001197 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001198 quiet=False,
1199 is_new=None,
1200 current_branch_only=False,
1201 force_sync=False,
1202 clone_bundle=True,
1203 no_tags=False,
1204 archive=False,
1205 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001206 prune=False,
1207 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001208 """Perform only the network IO portion of the sync process.
1209 Local working directory/branch state is not affected.
1210 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001211 if archive and not isinstance(self, MetaProject):
1212 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001213 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001214 return False
1215
1216 name = self.relpath.replace('\\', '/')
1217 name = name.replace('/', '_')
1218 tarpath = '%s.tar' % name
1219 topdir = self.manifest.topdir
1220
1221 try:
1222 self._FetchArchive(tarpath, cwd=topdir)
1223 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001224 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001225 return False
1226
1227 # From now on, we only need absolute tarpath
1228 tarpath = os.path.join(topdir, tarpath)
1229
1230 if not self._ExtractArchive(tarpath, path=topdir):
1231 return False
1232 try:
1233 os.remove(tarpath)
1234 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001235 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001236 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001237 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001238 if is_new is None:
1239 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001240 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001241 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001242 else:
1243 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001244 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001245
1246 if is_new:
1247 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1248 try:
1249 fd = open(alt, 'rb')
1250 try:
1251 alt_dir = fd.readline().rstrip()
1252 finally:
1253 fd.close()
1254 except IOError:
1255 alt_dir = None
1256 else:
1257 alt_dir = None
1258
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001259 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001260 and alt_dir is None \
1261 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001262 is_new = False
1263
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001264 if not current_branch_only:
1265 if self.sync_c:
1266 current_branch_only = True
1267 elif not self.manifest._loaded:
1268 # Manifest cannot check defaults until it syncs.
1269 current_branch_only = False
1270 elif self.manifest.default.sync_c:
1271 current_branch_only = True
1272
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001273 if self.clone_depth:
1274 depth = self.clone_depth
1275 else:
1276 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1277
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001278 need_to_fetch = not (optimized_fetch and
1279 (ID_RE.match(self.revisionExpr) and
1280 self._CheckForSha1()))
1281 if (need_to_fetch and
1282 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1283 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001284 no_tags=no_tags, prune=prune, depth=depth,
1285 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001286 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001287
1288 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001289 self._InitMRef()
1290 else:
1291 self._InitMirrorHead()
1292 try:
1293 os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
1294 except OSError:
1295 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001296 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001297
1298 def PostRepoUpgrade(self):
1299 self._InitHooks()
1300
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001301 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001302 if self.manifest.isGitcClient:
1303 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001304 for copyfile in self.copyfiles:
1305 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001306 for linkfile in self.linkfiles:
1307 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001308
Julien Camperguedd654222014-01-09 16:21:37 +01001309 def GetCommitRevisionId(self):
1310 """Get revisionId of a commit.
1311
1312 Use this method instead of GetRevisionId to get the id of the commit rather
1313 than the id of the current git object (for example, a tag)
1314
1315 """
1316 if not self.revisionExpr.startswith(R_TAGS):
1317 return self.GetRevisionId(self._allrefs)
1318
1319 try:
1320 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1321 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001322 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1323 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001324
David Pursehouse8a68ff92012-09-24 12:15:13 +09001325 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001326 if self.revisionId:
1327 return self.revisionId
1328
1329 rem = self.GetRemote(self.remote.name)
1330 rev = rem.ToLocal(self.revisionExpr)
1331
David Pursehouse8a68ff92012-09-24 12:15:13 +09001332 if all_refs is not None and rev in all_refs:
1333 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001334
1335 try:
1336 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1337 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001338 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1339 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001340
Martin Kellye4e94d22017-03-21 16:05:12 -07001341 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001342 """Perform only the local IO portion of the sync process.
1343 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001344 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001345 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001346 all_refs = self.bare_ref.all
1347 self.CleanPublishedCache(all_refs)
1348 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001349
David Pursehouse1d947b32012-10-25 12:23:11 +09001350 def _doff():
1351 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001352 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001353
Martin Kellye4e94d22017-03-21 16:05:12 -07001354 def _dosubmodules():
1355 self._SyncSubmodules(quiet=True)
1356
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001357 head = self.work_git.GetHead()
1358 if head.startswith(R_HEADS):
1359 branch = head[len(R_HEADS):]
1360 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001361 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001362 except KeyError:
1363 head = None
1364 else:
1365 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001366
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001367 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001368 # Currently on a detached HEAD. The user is assumed to
1369 # not have any local modifications worth worrying about.
1370 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001371 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001372 syncbuf.fail(self, _PriorSyncFailedError())
1373 return
1374
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001375 if head == revid:
1376 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001377 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001378 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001379 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001380 # The copy/linkfile config may have changed.
1381 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001382 return
1383 else:
1384 lost = self._revlist(not_rev(revid), HEAD)
1385 if lost:
1386 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001387
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001388 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001389 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001390 if submodules:
1391 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001392 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001393 syncbuf.fail(self, e)
1394 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001395 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001396 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001397
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001398 if head == revid:
1399 # No changes; don't do anything further.
1400 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001401 # The copy/linkfile config may have changed.
1402 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001403 return
1404
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001406
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001407 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001408 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001409 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001410 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001411 syncbuf.info(self,
1412 "leaving %s; does not track upstream",
1413 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001414 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001415 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001416 if submodules:
1417 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001418 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001419 syncbuf.fail(self, e)
1420 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001421 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001422 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001424 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001425 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001426 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001427 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001428 if not_merged:
1429 if upstream_gain:
1430 # The user has published this branch and some of those
1431 # commits are not yet merged upstream. We do not want
1432 # to rewrite the published commits so we punt.
1433 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001434 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001435 "branch %s is published (but not merged) and is now "
1436 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001437 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001438 elif pub == head:
1439 # All published commits are merged, and thus we are a
1440 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001441 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001442 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001443 if submodules:
1444 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001445 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001446
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001447 # Examine the local commits not in the remote. Find the
1448 # last one attributed to this user, if any.
1449 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001450 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001451 last_mine = None
1452 cnt_mine = 0
1453 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301454 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001455 if committer_email == self.UserEmail:
1456 last_mine = commit_id
1457 cnt_mine += 1
1458
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001459 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001460 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001461
1462 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001463 syncbuf.fail(self, _DirtyError())
1464 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001465
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001466 # If the upstream switched on us, warn the user.
1467 #
1468 if branch.merge != self.revisionExpr:
1469 if branch.merge and self.revisionExpr:
1470 syncbuf.info(self,
1471 'manifest switched %s...%s',
1472 branch.merge,
1473 self.revisionExpr)
1474 elif branch.merge:
1475 syncbuf.info(self,
1476 'manifest no longer tracks %s',
1477 branch.merge)
1478
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001479 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001480 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001481 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001482 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001483 syncbuf.info(self,
1484 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001485 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001486
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001487 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001488 if not ID_RE.match(self.revisionExpr):
1489 # in case of manifest sync the revisionExpr might be a SHA1
1490 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001491 if not branch.merge.startswith('refs/'):
1492 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001493 branch.Save()
1494
Mike Pontillod3153822012-02-28 11:53:24 -08001495 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001496 def _docopyandlink():
1497 self._CopyAndLinkFiles()
1498
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001499 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001500 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001501 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001502 if submodules:
1503 syncbuf.later2(self, _dosubmodules)
1504 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001505 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001506 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001507 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001508 if submodules:
1509 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001510 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001511 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001512 syncbuf.fail(self, e)
1513 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001514 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001515 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001516 if submodules:
1517 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001518
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001519 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001520 # dest should already be an absolute path, but src is project relative
1521 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001522 abssrc = os.path.join(self.worktree, src)
1523 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001524
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001525 def AddLinkFile(self, src, dest, absdest):
1526 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001527 # make src relative path to dest
1528 absdestdir = os.path.dirname(absdest)
1529 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001530 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001531
James W. Mills24c13082012-04-12 15:04:13 -05001532 def AddAnnotation(self, name, value, keep):
1533 self.annotations.append(_Annotation(name, value, keep))
1534
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001535 def DownloadPatchSet(self, change_id, patch_id):
1536 """Download a single patch set of a single change to FETCH_HEAD.
1537 """
1538 remote = self.GetRemote(self.remote.name)
1539
1540 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001541 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001542 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001543 if GitCommand(self, cmd, bare=True).Wait() != 0:
1544 return None
1545 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001546 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001547 change_id,
1548 patch_id,
1549 self.bare_git.rev_parse('FETCH_HEAD'))
1550
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001551
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001552# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001553
Simran Basib9a1b732015-08-20 12:19:28 -07001554 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001555 """Create a new branch off the manifest's revision.
1556 """
Simran Basib9a1b732015-08-20 12:19:28 -07001557 if not branch_merge:
1558 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001559 head = self.work_git.GetHead()
1560 if head == (R_HEADS + name):
1561 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001562
David Pursehouse8a68ff92012-09-24 12:15:13 +09001563 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001564 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001565 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001566 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001567 capture_stdout=True,
1568 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001569
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001570 branch = self.GetBranch(name)
1571 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001572 branch.merge = branch_merge
1573 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1574 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001575 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001576
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001577 if head.startswith(R_HEADS):
1578 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001579 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001580 except KeyError:
1581 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001582 if revid and head and revid == head:
1583 ref = os.path.join(self.gitdir, R_HEADS + name)
1584 try:
1585 os.makedirs(os.path.dirname(ref))
1586 except OSError:
1587 pass
1588 _lwrite(ref, '%s\n' % revid)
1589 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1590 'ref: %s%s\n' % (R_HEADS, name))
1591 branch.Save()
1592 return True
1593
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001594 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001595 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001596 capture_stdout=True,
1597 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001598 branch.Save()
1599 return True
1600 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001601
Wink Saville02d79452009-04-10 13:01:24 -07001602 def CheckoutBranch(self, name):
1603 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001604
1605 Args:
1606 name: The name of the branch to checkout.
1607
1608 Returns:
1609 True if the checkout succeeded; False if it didn't; None if the branch
1610 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001611 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001612 rev = R_HEADS + name
1613 head = self.work_git.GetHead()
1614 if head == rev:
1615 # Already on the branch
1616 #
1617 return True
Wink Saville02d79452009-04-10 13:01:24 -07001618
David Pursehouse8a68ff92012-09-24 12:15:13 +09001619 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001620 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001621 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001622 except KeyError:
1623 # Branch does not exist in this project
1624 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001625 return None
Wink Saville02d79452009-04-10 13:01:24 -07001626
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001627 if head.startswith(R_HEADS):
1628 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001629 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001630 except KeyError:
1631 head = None
1632
1633 if head == revid:
1634 # Same revision; just update HEAD to point to the new
1635 # target branch, but otherwise take no other action.
1636 #
1637 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1638 'ref: %s%s\n' % (R_HEADS, name))
1639 return True
1640
1641 return GitCommand(self,
1642 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001643 capture_stdout=True,
1644 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001645
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001646 def AbandonBranch(self, name):
1647 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001648
1649 Args:
1650 name: The name of the branch to abandon.
1651
1652 Returns:
1653 True if the abandon succeeded; False if it didn't; None if the branch
1654 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001655 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001656 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001657 all_refs = self.bare_ref.all
1658 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001659 # Doesn't exist
1660 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001661
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001662 head = self.work_git.GetHead()
1663 if head == rev:
1664 # We can't destroy the branch while we are sitting
1665 # on it. Switch to a detached HEAD.
1666 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001667 head = all_refs[head]
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001668
David Pursehouse8a68ff92012-09-24 12:15:13 +09001669 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001670 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001671 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1672 '%s\n' % revid)
1673 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001674 self._Checkout(revid, quiet=True)
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001675
1676 return GitCommand(self,
1677 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001678 capture_stdout=True,
1679 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001680
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001681 def PruneHeads(self):
1682 """Prune any topic branches already merged into upstream.
1683 """
1684 cb = self.CurrentBranch
1685 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001686 left = self._allrefs
1687 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001688 if name.startswith(R_HEADS):
1689 name = name[len(R_HEADS):]
1690 if cb is None or name != cb:
1691 kill.append(name)
1692
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001693 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001694 if cb is not None \
1695 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001696 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001697 self.work_git.DetachHead(HEAD)
1698 kill.append(cb)
1699
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001700 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001701 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001702
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001703 try:
1704 self.bare_git.DetachHead(rev)
1705
1706 b = ['branch', '-d']
1707 b.extend(kill)
1708 b = GitCommand(self, b, bare=True,
1709 capture_stdout=True,
1710 capture_stderr=True)
1711 b.Wait()
1712 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001713 if ID_RE.match(old):
1714 self.bare_git.DetachHead(old)
1715 else:
1716 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001717 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001718
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001719 for branch in kill:
1720 if (R_HEADS + branch) not in left:
1721 self.CleanPublishedCache()
1722 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001723
1724 if cb and cb not in kill:
1725 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001726 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001727
1728 kept = []
1729 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001730 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001731 branch = self.GetBranch(branch)
1732 base = branch.LocalMerge
1733 if not base:
1734 base = rev
1735 kept.append(ReviewableBranch(self, branch, base))
1736 return kept
1737
1738
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001739# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001740
1741 def GetRegisteredSubprojects(self):
1742 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001743
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001744 def rec(subprojects):
1745 if not subprojects:
1746 return
1747 result.extend(subprojects)
1748 for p in subprojects:
1749 rec(p.subprojects)
1750 rec(self.subprojects)
1751 return result
1752
1753 def _GetSubmodules(self):
1754 # Unfortunately we cannot call `git submodule status --recursive` here
1755 # because the working tree might not exist yet, and it cannot be used
1756 # without a working tree in its current implementation.
1757
1758 def get_submodules(gitdir, rev):
1759 # Parse .gitmodules for submodule sub_paths and sub_urls
1760 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1761 if not sub_paths:
1762 return []
1763 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1764 # revision of submodule repository
1765 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1766 submodules = []
1767 for sub_path, sub_url in zip(sub_paths, sub_urls):
1768 try:
1769 sub_rev = sub_revs[sub_path]
1770 except KeyError:
1771 # Ignore non-exist submodules
1772 continue
1773 submodules.append((sub_rev, sub_path, sub_url))
1774 return submodules
1775
1776 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1777 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001778
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001779 def parse_gitmodules(gitdir, rev):
1780 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1781 try:
Anthony King7bdac712014-07-16 12:56:40 +01001782 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1783 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001784 except GitError:
1785 return [], []
1786 if p.Wait() != 0:
1787 return [], []
1788
1789 gitmodules_lines = []
1790 fd, temp_gitmodules_path = tempfile.mkstemp()
1791 try:
1792 os.write(fd, p.stdout)
1793 os.close(fd)
1794 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001795 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1796 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001797 if p.Wait() != 0:
1798 return [], []
1799 gitmodules_lines = p.stdout.split('\n')
1800 except GitError:
1801 return [], []
1802 finally:
1803 os.remove(temp_gitmodules_path)
1804
1805 names = set()
1806 paths = {}
1807 urls = {}
1808 for line in gitmodules_lines:
1809 if not line:
1810 continue
1811 m = re_path.match(line)
1812 if m:
1813 names.add(m.group(1))
1814 paths[m.group(1)] = m.group(2)
1815 continue
1816 m = re_url.match(line)
1817 if m:
1818 names.add(m.group(1))
1819 urls[m.group(1)] = m.group(2)
1820 continue
1821 names = sorted(names)
1822 return ([paths.get(name, '') for name in names],
1823 [urls.get(name, '') for name in names])
1824
1825 def git_ls_tree(gitdir, rev, paths):
1826 cmd = ['ls-tree', rev, '--']
1827 cmd.extend(paths)
1828 try:
Anthony King7bdac712014-07-16 12:56:40 +01001829 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1830 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001831 except GitError:
1832 return []
1833 if p.Wait() != 0:
1834 return []
1835 objects = {}
1836 for line in p.stdout.split('\n'):
1837 if not line.strip():
1838 continue
1839 object_rev, object_path = line.split()[2:4]
1840 objects[object_path] = object_rev
1841 return objects
1842
1843 try:
1844 rev = self.GetRevisionId()
1845 except GitError:
1846 return []
1847 return get_submodules(self.gitdir, rev)
1848
1849 def GetDerivedSubprojects(self):
1850 result = []
1851 if not self.Exists:
1852 # If git repo does not exist yet, querying its submodules will
1853 # mess up its states; so return here.
1854 return result
1855 for rev, path, url in self._GetSubmodules():
1856 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001857 relpath, worktree, gitdir, objdir = \
1858 self.manifest.GetSubprojectPaths(self, name, path)
1859 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001860 if project:
1861 result.extend(project.GetDerivedSubprojects())
1862 continue
David James8d201162013-10-11 17:03:19 -07001863
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001864 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001865 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001866 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001867 review=self.remote.review,
1868 revision=self.remote.revision)
1869 subproject = Project(manifest=self.manifest,
1870 name=name,
1871 remote=remote,
1872 gitdir=gitdir,
1873 objdir=objdir,
1874 worktree=worktree,
1875 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001876 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001877 revisionId=rev,
1878 rebase=self.rebase,
1879 groups=self.groups,
1880 sync_c=self.sync_c,
1881 sync_s=self.sync_s,
1882 parent=self,
1883 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001884 result.append(subproject)
1885 result.extend(subproject.GetDerivedSubprojects())
1886 return result
1887
1888
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001889# Direct Git Commands ##
Chris AtLee2fb64662014-01-16 21:32:33 -05001890 def _CheckForSha1(self):
1891 try:
1892 # if revision (sha or tag) is not present then following function
1893 # throws an error.
1894 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1895 return True
1896 except GitError:
1897 # There is no such persistent revision. We have to fetch it.
1898 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001899
Julien Campergue335f5ef2013-10-16 11:02:35 +02001900 def _FetchArchive(self, tarpath, cwd=None):
1901 cmd = ['archive', '-v', '-o', tarpath]
1902 cmd.append('--remote=%s' % self.remote.url)
1903 cmd.append('--prefix=%s/' % self.relpath)
1904 cmd.append(self.revisionExpr)
1905
1906 command = GitCommand(self, cmd, cwd=cwd,
1907 capture_stdout=True,
1908 capture_stderr=True)
1909
1910 if command.Wait() != 0:
1911 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1912
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001913 def _RemoteFetch(self, name=None,
1914 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001915 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001916 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001917 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001918 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001919 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001920 depth=None,
1921 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001922
1923 is_sha1 = False
1924 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001925 # The depth should not be used when fetching to a mirror because
1926 # it will result in a shallow repository that cannot be cloned or
1927 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001928 # The repo project should also never be synced with partial depth.
1929 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1930 depth = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001931
Shawn Pearce69e04d82014-01-29 12:48:54 -08001932 if depth:
1933 current_branch_only = True
1934
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001935 if ID_RE.match(self.revisionExpr) is not None:
1936 is_sha1 = True
1937
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001938 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001939 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001940 # this is a tag and its sha1 value should never change
1941 tag_name = self.revisionExpr[len(R_TAGS):]
1942
1943 if is_sha1 or tag_name is not None:
Chris AtLee2fb64662014-01-16 21:32:33 -05001944 if self._CheckForSha1():
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001945 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001946 if is_sha1 and not depth:
1947 # When syncing a specific commit and --depth is not set:
1948 # * if upstream is explicitly specified and is not a sha1, fetch only
1949 # upstream as users expect only upstream to be fetch.
1950 # Note: The commit might not be in upstream in which case the sync
1951 # will fail.
1952 # * otherwise, fetch all branches to make sure we end up with the
1953 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001954 if self.upstream:
1955 current_branch_only = not ID_RE.match(self.upstream)
1956 else:
1957 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001958
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001959 if not name:
1960 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001961
1962 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001963 remote = self.GetRemote(name)
1964 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001965 ssh_proxy = True
1966
Shawn O. Pearce88443382010-10-08 10:02:09 +02001967 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001968 if alt_dir and 'objects' == os.path.basename(alt_dir):
1969 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001970 packed_refs = os.path.join(self.gitdir, 'packed-refs')
1971 remote = self.GetRemote(name)
1972
David Pursehouse8a68ff92012-09-24 12:15:13 +09001973 all_refs = self.bare_ref.all
1974 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02001975 tmp = set()
1976
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301977 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001978 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001979 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001980 all_refs[r] = ref_id
1981 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001982 continue
1983
David Pursehouse8a68ff92012-09-24 12:15:13 +09001984 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001985 continue
1986
David Pursehouse8a68ff92012-09-24 12:15:13 +09001987 r = 'refs/_alt/%s' % ref_id
1988 all_refs[r] = ref_id
1989 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001990 tmp.add(r)
1991
heping3d7bbc92017-04-12 19:51:47 +08001992 tmp_packed_lines = []
1993 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02001994
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301995 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001996 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08001997 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001998 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08001999 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002000
heping3d7bbc92017-04-12 19:51:47 +08002001 tmp_packed = ''.join(tmp_packed_lines)
2002 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002003 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002004 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002005 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002006
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002007 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002008
Conley Owensf97e8382015-01-21 11:12:46 -08002009 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002010 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002011 else:
2012 # If this repo has shallow objects, then we don't know which refs have
2013 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2014 # do this with projects that don't have shallow objects, since it is less
2015 # efficient.
2016 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2017 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002018
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002019 if quiet:
2020 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002021 if not self.worktree:
2022 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002023 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002024
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002025 # If using depth then we should not get all the tags since they may
2026 # be outside of the depth.
2027 if no_tags or depth:
2028 cmd.append('--no-tags')
2029 else:
2030 cmd.append('--tags')
2031
David Pursehouse74cfd272015-10-14 10:50:15 +09002032 if prune:
2033 cmd.append('--prune')
2034
Martin Kellye4e94d22017-03-21 16:05:12 -07002035 if submodules:
2036 cmd.append('--recurse-submodules=on-demand')
2037
Conley Owens80b87fe2014-05-09 17:13:44 -07002038 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002039 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002040 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002041 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002042 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002043 spec.append('tag')
2044 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002045
David Pursehouse403b64e2015-04-27 10:41:33 +09002046 if not self.manifest.IsMirror:
2047 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002048 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002049 # Shallow checkout of a specific commit, fetch from that commit and not
2050 # the heads only as the commit might be deeper in the history.
2051 spec.append(branch)
2052 else:
2053 if is_sha1:
2054 branch = self.upstream
2055 if branch is not None and branch.strip():
2056 if not branch.startswith('refs/'):
2057 branch = R_HEADS + branch
2058 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002059 cmd.extend(spec)
2060
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002061 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002062 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002063 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002064 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002065 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002066 ok = True
2067 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002068 # If needed, run the 'git remote prune' the first time through the loop
2069 elif (not _i and
2070 "error:" in gitcmd.stderr and
2071 "git remote prune" in gitcmd.stderr):
2072 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002073 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002074 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002075 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002076 break
2077 continue
Brian Harring14a66742012-09-28 20:21:57 -07002078 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002079 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2080 # in sha1 mode, we just tried sync'ing from the upstream field; it
2081 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002082 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002083 elif ret < 0:
2084 # Git died with a signal, exit immediately
2085 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002086 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002087
2088 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002089 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002090 if old_packed != '':
2091 _lwrite(packed_refs, old_packed)
2092 else:
2093 os.remove(packed_refs)
2094 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002095
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002096 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002097 # We just synced the upstream given branch; verify we
2098 # got what we wanted, else trigger a second run of all
2099 # refs.
Chris AtLee2fb64662014-01-16 21:32:33 -05002100 if not self._CheckForSha1():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002101 if current_branch_only and depth:
2102 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002103 return self._RemoteFetch(name=name,
2104 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002105 initial=False, quiet=quiet, alt_dir=alt_dir,
2106 depth=None)
2107 else:
2108 # Avoid infinite recursion: sync all branches with depth set to None
2109 return self._RemoteFetch(name=name, current_branch_only=False,
2110 initial=False, quiet=quiet, alt_dir=alt_dir,
2111 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002112
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002113 return ok
Shawn O. Pearce88443382010-10-08 10:02:09 +02002114
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002115 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002116 if initial and \
2117 (self.manifest.manifestProject.config.GetString('repo.depth') or
2118 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002119 return False
2120
2121 remote = self.GetRemote(self.remote.name)
2122 bundle_url = remote.url + '/clone.bundle'
2123 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002124 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2125 'persistent-http',
2126 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002127 return False
2128
2129 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2130 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
2131
2132 exist_dst = os.path.exists(bundle_dst)
2133 exist_tmp = os.path.exists(bundle_tmp)
2134
2135 if not initial and not exist_dst and not exist_tmp:
2136 return False
2137
2138 if not exist_dst:
2139 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2140 if not exist_dst:
2141 return False
2142
2143 cmd = ['fetch']
2144 if quiet:
2145 cmd.append('--quiet')
2146 if not self.worktree:
2147 cmd.append('--update-head-ok')
2148 cmd.append(bundle_dst)
2149 for f in remote.fetch:
2150 cmd.append(str(f))
2151 cmd.append('refs/tags/*:refs/tags/*')
2152
2153 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002154 if os.path.exists(bundle_dst):
2155 os.remove(bundle_dst)
2156 if os.path.exists(bundle_tmp):
2157 os.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002158 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002159
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002160 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002161 if os.path.exists(dstPath):
2162 os.remove(dstPath)
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002163
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002164 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002165 if quiet:
2166 cmd += ['--silent']
2167 if os.path.exists(tmpPath):
2168 size = os.stat(tmpPath).st_size
2169 if size >= 1024:
2170 cmd += ['--continue-at', '%d' % (size,)]
2171 else:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002172 os.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002173 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2174 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002175 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002176 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002177 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002178 if srcUrl.startswith('persistent-'):
2179 srcUrl = srcUrl[len('persistent-'):]
2180 cmd += [srcUrl]
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002181
Dave Borowitz137d0132015-01-02 11:12:54 -08002182 if IsTrace():
2183 Trace('%s', ' '.join(cmd))
2184 try:
2185 proc = subprocess.Popen(cmd)
2186 except OSError:
2187 return False
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002188
Dave Borowitz137d0132015-01-02 11:12:54 -08002189 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002190
Dave Borowitz137d0132015-01-02 11:12:54 -08002191 if curlret == 22:
2192 # From curl man page:
2193 # 22: HTTP page not retrieved. The requested url was not found or
2194 # returned another error with the HTTP error code being 400 or above.
2195 # This return code only appears if -f, --fail is used.
2196 if not quiet:
2197 print("Server does not provide clone.bundle; ignoring.",
2198 file=sys.stderr)
2199 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002200
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002201 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002202 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002203 os.rename(tmpPath, dstPath)
2204 return True
2205 else:
2206 os.remove(tmpPath)
2207 return False
2208 else:
2209 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002210
Kris Giesingc8d882a2014-12-23 13:02:32 -08002211 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002212 try:
2213 with open(path) as f:
2214 if f.read(16) == '# v2 git bundle\n':
2215 return True
2216 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002217 if not quiet:
2218 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002219 return False
2220 except OSError:
2221 return False
2222
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002223 def _Checkout(self, rev, quiet=False):
2224 cmd = ['checkout']
2225 if quiet:
2226 cmd.append('-q')
2227 cmd.append(rev)
2228 cmd.append('--')
2229 if GitCommand(self, cmd).Wait() != 0:
2230 if self._allrefs:
2231 raise GitError('%s checkout %s ' % (self.name, rev))
2232
Anthony King7bdac712014-07-16 12:56:40 +01002233 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002234 cmd = ['cherry-pick']
2235 cmd.append(rev)
2236 cmd.append('--')
2237 if GitCommand(self, cmd).Wait() != 0:
2238 if self._allrefs:
2239 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2240
Anthony King7bdac712014-07-16 12:56:40 +01002241 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002242 cmd = ['revert']
2243 cmd.append('--no-edit')
2244 cmd.append(rev)
2245 cmd.append('--')
2246 if GitCommand(self, cmd).Wait() != 0:
2247 if self._allrefs:
2248 raise GitError('%s revert %s ' % (self.name, rev))
2249
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002250 def _ResetHard(self, rev, quiet=True):
2251 cmd = ['reset', '--hard']
2252 if quiet:
2253 cmd.append('-q')
2254 cmd.append(rev)
2255 if GitCommand(self, cmd).Wait() != 0:
2256 raise GitError('%s reset --hard %s ' % (self.name, rev))
2257
Martin Kellye4e94d22017-03-21 16:05:12 -07002258 def _SyncSubmodules(self, quiet=True):
2259 cmd = ['submodule', 'update', '--init', '--recursive']
2260 if quiet:
2261 cmd.append('-q')
2262 if GitCommand(self, cmd).Wait() != 0:
2263 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2264
Anthony King7bdac712014-07-16 12:56:40 +01002265 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002266 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002267 if onto is not None:
2268 cmd.extend(['--onto', onto])
2269 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002270 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002271 raise GitError('%s rebase %s ' % (self.name, upstream))
2272
Pierre Tardy3d125942012-05-04 12:18:12 +02002273 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002274 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002275 if ffonly:
2276 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002277 if GitCommand(self, cmd).Wait() != 0:
2278 raise GitError('%s merge %s ' % (self.name, head))
2279
Kevin Degiabaa7f32014-11-12 11:27:45 -07002280 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002281 init_git_dir = not os.path.exists(self.gitdir)
2282 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002283 try:
2284 # Initialize the bare repository, which contains all of the objects.
2285 if init_obj_dir:
2286 os.makedirs(self.objdir)
2287 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002288
Kevin Degib1a07b82015-07-27 13:33:43 -06002289 # If we have a separate directory to hold refs, initialize it as well.
2290 if self.objdir != self.gitdir:
2291 if init_git_dir:
2292 os.makedirs(self.gitdir)
2293
2294 if init_obj_dir or init_git_dir:
2295 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2296 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002297 try:
2298 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2299 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002300 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002301 print("Retrying clone after deleting %s" %
2302 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002303 try:
2304 shutil.rmtree(os.path.realpath(self.gitdir))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002305 if self.worktree and os.path.exists(os.path.realpath
2306 (self.worktree)):
Kevin Degiabaa7f32014-11-12 11:27:45 -07002307 shutil.rmtree(os.path.realpath(self.worktree))
2308 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2309 except:
2310 raise e
2311 raise e
Kevin Degib1a07b82015-07-27 13:33:43 -06002312
Kevin Degi384b3c52014-10-16 16:02:58 -06002313 if init_git_dir:
Kevin Degib1a07b82015-07-27 13:33:43 -06002314 mp = self.manifest.manifestProject
2315 ref_dir = mp.config.GetString('repo.reference') or ''
Kevin Degi384b3c52014-10-16 16:02:58 -06002316
Kevin Degib1a07b82015-07-27 13:33:43 -06002317 if ref_dir or mirror_git:
2318 if not mirror_git:
2319 mirror_git = os.path.join(ref_dir, self.name + '.git')
2320 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2321 self.relpath + '.git')
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002322
Kevin Degib1a07b82015-07-27 13:33:43 -06002323 if os.path.exists(mirror_git):
2324 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002325
Kevin Degib1a07b82015-07-27 13:33:43 -06002326 elif os.path.exists(repo_git):
2327 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002328
Kevin Degib1a07b82015-07-27 13:33:43 -06002329 else:
2330 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002331
Kevin Degib1a07b82015-07-27 13:33:43 -06002332 if ref_dir:
2333 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2334 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002335
Kevin Degib1a07b82015-07-27 13:33:43 -06002336 self._UpdateHooks()
2337
2338 m = self.manifest.manifestProject.config
2339 for key in ['user.name', 'user.email']:
2340 if m.Has(key, include_defaults=False):
2341 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002342 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002343 if self.manifest.IsMirror:
2344 self.config.SetString('core.bare', 'true')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002345 else:
Kevin Degib1a07b82015-07-27 13:33:43 -06002346 self.config.SetString('core.bare', None)
2347 except Exception:
2348 if init_obj_dir and os.path.exists(self.objdir):
2349 shutil.rmtree(self.objdir)
2350 if init_git_dir and os.path.exists(self.gitdir):
2351 shutil.rmtree(self.gitdir)
2352 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002353
Jimmie Westera0444582012-10-24 13:44:42 +02002354 def _UpdateHooks(self):
2355 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002356 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002357
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002358 def _InitHooks(self):
Jesse Hall672cc492013-11-27 11:17:13 -08002359 hooks = os.path.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002360 if not os.path.exists(hooks):
2361 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002362 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002363 name = os.path.basename(stock_hook)
2364
Victor Boivie65e0f352011-04-18 11:23:29 +02002365 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002366 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002367 # Don't install a Gerrit Code Review hook if this
2368 # project does not appear to use it for reviews.
2369 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002370 # Since the manifest project is one of those, but also
2371 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002372 continue
2373
2374 dst = os.path.join(hooks, name)
2375 if os.path.islink(dst):
2376 continue
2377 if os.path.exists(dst):
2378 if filecmp.cmp(stock_hook, dst, shallow=False):
2379 os.remove(dst)
2380 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002381 _warn("%s: Not replacing locally modified %s hook",
2382 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002383 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002384 try:
Mickaël Salaünb9477bc2012-08-05 13:39:26 +02002385 os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002386 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002387 if e.errno == errno.EPERM:
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002388 raise GitError('filesystem must support symlinks')
2389 else:
2390 raise
2391
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002392 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002393 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002394 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002395 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002396 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002397 remote.review = self.remote.review
2398 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002399
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002400 if self.worktree:
2401 remote.ResetFetch(mirror=False)
2402 else:
2403 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002404 remote.Save()
2405
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002406 def _InitMRef(self):
2407 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002408 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002409
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002410 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002411 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002412
2413 def _InitAnyMRef(self, ref):
2414 cur = self.bare_ref.symref(ref)
2415
2416 if self.revisionId:
2417 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2418 msg = 'manifest set to %s' % self.revisionId
2419 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002420 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002421 else:
2422 remote = self.GetRemote(self.remote.name)
2423 dst = remote.ToLocal(self.revisionExpr)
2424 if cur != dst:
2425 msg = 'manifest set to %s' % self.revisionExpr
2426 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002427
Kevin Degi384b3c52014-10-16 16:02:58 -06002428 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002429 symlink_files = self.shareable_files[:]
2430 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002431 if share_refs:
2432 symlink_files += self.working_tree_files
2433 symlink_dirs += self.working_tree_dirs
2434 to_symlink = symlink_files + symlink_dirs
2435 for name in set(to_symlink):
2436 dst = os.path.realpath(os.path.join(destdir, name))
2437 if os.path.lexists(dst):
2438 src = os.path.realpath(os.path.join(srcdir, name))
2439 # Fail if the links are pointing to the wrong place
2440 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002441 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002442 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002443 'work tree. If you\'re comfortable with the '
2444 'possibility of losing the work tree\'s git metadata,'
2445 ' use `repo sync --force-sync {0}` to '
2446 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002447
David James8d201162013-10-11 17:03:19 -07002448 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2449 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2450
2451 Args:
2452 gitdir: The bare git repository. Must already be initialized.
2453 dotgit: The repository you would like to initialize.
2454 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2455 Only one work tree can store refs under a given |gitdir|.
2456 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2457 This saves you the effort of initializing |dotgit| yourself.
2458 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002459 symlink_files = self.shareable_files[:]
2460 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002461 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002462 symlink_files += self.working_tree_files
2463 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002464 to_symlink = symlink_files + symlink_dirs
2465
2466 to_copy = []
2467 if copy_all:
2468 to_copy = os.listdir(gitdir)
2469
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002470 dotgit = os.path.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002471 for name in set(to_copy).union(to_symlink):
2472 try:
2473 src = os.path.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002474 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002475
Kevin Degi384b3c52014-10-16 16:02:58 -06002476 if os.path.lexists(dst):
2477 continue
David James8d201162013-10-11 17:03:19 -07002478
2479 # If the source dir doesn't exist, create an empty dir.
2480 if name in symlink_dirs and not os.path.lexists(src):
2481 os.makedirs(src)
2482
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002483 if name in to_symlink:
2484 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
2485 elif copy_all and not os.path.islink(dst):
2486 if os.path.isdir(src):
2487 shutil.copytree(src, dst)
2488 elif os.path.isfile(src):
2489 shutil.copy(src, dst)
2490
Conley Owens80b87fe2014-05-09 17:13:44 -07002491 # If the source file doesn't exist, ensure the destination
2492 # file doesn't either.
2493 if name in symlink_files and not os.path.lexists(src):
2494 try:
2495 os.remove(dst)
2496 except OSError:
2497 pass
2498
David James8d201162013-10-11 17:03:19 -07002499 except OSError as e:
2500 if e.errno == errno.EPERM:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002501 raise DownloadError('filesystem must support symlinks')
David James8d201162013-10-11 17:03:19 -07002502 else:
2503 raise
2504
Martin Kellye4e94d22017-03-21 16:05:12 -07002505 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002506 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002507 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002508 try:
2509 if init_dotgit:
2510 os.makedirs(dotgit)
2511 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2512 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002513
Kevin Degiabaa7f32014-11-12 11:27:45 -07002514 try:
2515 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2516 except GitError as e:
2517 if force_sync:
2518 try:
2519 shutil.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002520 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002521 except:
2522 raise e
2523 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002524
Kevin Degib1a07b82015-07-27 13:33:43 -06002525 if init_dotgit:
2526 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002527
Kevin Degib1a07b82015-07-27 13:33:43 -06002528 cmd = ['read-tree', '--reset', '-u']
2529 cmd.append('-v')
2530 cmd.append(HEAD)
2531 if GitCommand(self, cmd).Wait() != 0:
2532 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002533
Martin Kellye4e94d22017-03-21 16:05:12 -07002534 if submodules:
2535 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002536 self._CopyAndLinkFiles()
2537 except Exception:
2538 if init_dotgit:
2539 shutil.rmtree(dotgit)
2540 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002541
2542 def _gitdir_path(self, path):
David James8d201162013-10-11 17:03:19 -07002543 return os.path.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002544
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002545 def _revlist(self, *args, **kw):
2546 a = []
2547 a.extend(args)
2548 a.append('--')
2549 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002550
2551 @property
2552 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002553 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002554
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002555 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002556 """Get logs between two revisions of this project."""
2557 comp = '..'
2558 if rev1:
2559 revs = [rev1]
2560 if rev2:
2561 revs.extend([comp, rev2])
2562 cmd = ['log', ''.join(revs)]
2563 out = DiffColoring(self.config)
2564 if out.is_on and color:
2565 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002566 if pretty_format is not None:
2567 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002568 if oneline:
2569 cmd.append('--oneline')
2570
2571 try:
2572 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2573 if log.Wait() == 0:
2574 return log.stdout
2575 except GitError:
2576 # worktree may not exist if groups changed for example. In that case,
2577 # try in gitdir instead.
2578 if not os.path.exists(self.worktree):
2579 return self.bare_git.log(*cmd[1:])
2580 else:
2581 raise
2582 return None
2583
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002584 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2585 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002586 """Get the list of logs from this revision to given revisionId"""
2587 logs = {}
2588 selfId = self.GetRevisionId(self._allrefs)
2589 toId = toProject.GetRevisionId(toProject._allrefs)
2590
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002591 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2592 pretty_format=pretty_format)
2593 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2594 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002595 return logs
2596
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002597 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002598
David James8d201162013-10-11 17:03:19 -07002599 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002600 self._project = project
2601 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002602 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002603
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002604 def LsOthers(self):
2605 p = GitCommand(self._project,
2606 ['ls-files',
2607 '-z',
2608 '--others',
2609 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002610 bare=False,
David James8d201162013-10-11 17:03:19 -07002611 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002612 capture_stdout=True,
2613 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002614 if p.Wait() == 0:
2615 out = p.stdout
2616 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002617 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002618 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002619 return []
2620
2621 def DiffZ(self, name, *args):
2622 cmd = [name]
2623 cmd.append('-z')
2624 cmd.extend(args)
2625 p = GitCommand(self._project,
2626 cmd,
David James8d201162013-10-11 17:03:19 -07002627 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002628 bare=False,
2629 capture_stdout=True,
2630 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002631 try:
2632 out = p.process.stdout.read()
2633 r = {}
2634 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002635 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002636 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002637 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002638 info = next(out)
2639 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002640 except StopIteration:
2641 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002642
2643 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002644
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002645 def __init__(self, path, omode, nmode, oid, nid, state):
2646 self.path = path
2647 self.src_path = None
2648 self.old_mode = omode
2649 self.new_mode = nmode
2650 self.old_id = oid
2651 self.new_id = nid
2652
2653 if len(state) == 1:
2654 self.status = state
2655 self.level = None
2656 else:
2657 self.status = state[:1]
2658 self.level = state[1:]
2659 while self.level.startswith('0'):
2660 self.level = self.level[1:]
2661
2662 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002663 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002664 if info.status in ('R', 'C'):
2665 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002666 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002667 r[info.path] = info
2668 return r
2669 finally:
2670 p.Wait()
2671
2672 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002673 if self._bare:
2674 path = os.path.join(self._project.gitdir, HEAD)
2675 else:
2676 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002677 try:
2678 fd = open(path, 'rb')
Dan Sandler53e902a2014-03-09 13:20:02 -04002679 except IOError as e:
2680 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002681 try:
2682 line = fd.read()
2683 finally:
2684 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302685 try:
2686 line = line.decode()
2687 except AttributeError:
2688 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002689 if line.startswith('ref: '):
2690 return line[5:-1]
2691 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002692
2693 def SetHead(self, ref, message=None):
2694 cmdv = []
2695 if message is not None:
2696 cmdv.extend(['-m', message])
2697 cmdv.append(HEAD)
2698 cmdv.append(ref)
2699 self.symbolic_ref(*cmdv)
2700
2701 def DetachHead(self, new, message=None):
2702 cmdv = ['--no-deref']
2703 if message is not None:
2704 cmdv.extend(['-m', message])
2705 cmdv.append(HEAD)
2706 cmdv.append(new)
2707 self.update_ref(*cmdv)
2708
2709 def UpdateRef(self, name, new, old=None,
2710 message=None,
2711 detach=False):
2712 cmdv = []
2713 if message is not None:
2714 cmdv.extend(['-m', message])
2715 if detach:
2716 cmdv.append('--no-deref')
2717 cmdv.append(name)
2718 cmdv.append(new)
2719 if old is not None:
2720 cmdv.append(old)
2721 self.update_ref(*cmdv)
2722
2723 def DeleteRef(self, name, old=None):
2724 if not old:
2725 old = self.rev_parse(name)
2726 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002727 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002728
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002729 def rev_list(self, *args, **kw):
2730 if 'format' in kw:
2731 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2732 else:
2733 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002734 cmdv.extend(args)
2735 p = GitCommand(self._project,
2736 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002737 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002738 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002739 capture_stdout=True,
2740 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741 r = []
2742 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002743 if line[-1] == '\n':
2744 line = line[:-1]
2745 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002746 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002747 raise GitError('%s rev-list %s: %s' %
2748 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002749 return r
2750
2751 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002752 """Allow arbitrary git commands using pythonic syntax.
2753
2754 This allows you to do things like:
2755 git_obj.rev_parse('HEAD')
2756
2757 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2758 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002759 Any other positional arguments will be passed to the git command, and the
2760 following keyword arguments are supported:
2761 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002762
2763 Args:
2764 name: The name of the git command to call. Any '_' characters will
2765 be replaced with '-'.
2766
2767 Returns:
2768 A callable object that will try to call git with the named command.
2769 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002770 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002771
Dave Borowitz091f8932012-10-23 17:01:04 -07002772 def runner(*args, **kwargs):
2773 cmdv = []
2774 config = kwargs.pop('config', None)
2775 for k in kwargs:
2776 raise TypeError('%s() got an unexpected keyword argument %r'
2777 % (name, k))
2778 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002779 if not git_require((1, 7, 2)):
2780 raise ValueError('cannot set config on command line for %s()'
2781 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302782 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002783 cmdv.append('-c')
2784 cmdv.append('%s=%s' % (k, v))
2785 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002786 cmdv.extend(args)
2787 p = GitCommand(self._project,
2788 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002789 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002790 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002791 capture_stdout=True,
2792 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002793 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002794 raise GitError('%s %s: %s' %
2795 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002796 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302797 try:
Conley Owensedd01512013-09-26 12:59:58 -07002798 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302799 except AttributeError:
2800 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002801 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2802 return r[:-1]
2803 return r
2804 return runner
2805
2806
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002807class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002808
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002809 def __str__(self):
2810 return 'prior sync failed; rebase still in progress'
2811
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002812
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002813class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002814
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002815 def __str__(self):
2816 return 'contains uncommitted changes'
2817
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002818
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002819class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002820
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002821 def __init__(self, project, text):
2822 self.project = project
2823 self.text = text
2824
2825 def Print(self, syncbuf):
2826 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2827 syncbuf.out.nl()
2828
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002829
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002830class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002831
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002832 def __init__(self, project, why):
2833 self.project = project
2834 self.why = why
2835
2836 def Print(self, syncbuf):
2837 syncbuf.out.fail('error: %s/: %s',
2838 self.project.relpath,
2839 str(self.why))
2840 syncbuf.out.nl()
2841
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002842
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002843class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002844
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002845 def __init__(self, project, action):
2846 self.project = project
2847 self.action = action
2848
2849 def Run(self, syncbuf):
2850 out = syncbuf.out
2851 out.project('project %s/', self.project.relpath)
2852 out.nl()
2853 try:
2854 self.action()
2855 out.nl()
2856 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002857 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002858 out.nl()
2859 return False
2860
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002861
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002862class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002863
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002864 def __init__(self, config):
2865 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002866 self.project = self.printer('header', attr='bold')
2867 self.info = self.printer('info')
2868 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002869
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002870
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002871class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002872
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002873 def __init__(self, config, detach_head=False):
2874 self._messages = []
2875 self._failures = []
2876 self._later_queue1 = []
2877 self._later_queue2 = []
2878
2879 self.out = _SyncColoring(config)
2880 self.out.redirect(sys.stderr)
2881
2882 self.detach_head = detach_head
2883 self.clean = True
David Rileye0684ad2017-04-05 00:02:59 -07002884 self.recent_clean = True
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002885
2886 def info(self, project, fmt, *args):
2887 self._messages.append(_InfoMessage(project, fmt % args))
2888
2889 def fail(self, project, err=None):
2890 self._failures.append(_Failure(project, err))
David Rileye0684ad2017-04-05 00:02:59 -07002891 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002892
2893 def later1(self, project, what):
2894 self._later_queue1.append(_Later(project, what))
2895
2896 def later2(self, project, what):
2897 self._later_queue2.append(_Later(project, what))
2898
2899 def Finish(self):
2900 self._PrintMessages()
2901 self._RunLater()
2902 self._PrintMessages()
2903 return self.clean
2904
David Rileye0684ad2017-04-05 00:02:59 -07002905 def Recently(self):
2906 recent_clean = self.recent_clean
2907 self.recent_clean = True
2908 return recent_clean
2909
2910 def _MarkUnclean(self):
2911 self.clean = False
2912 self.recent_clean = False
2913
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002914 def _RunLater(self):
2915 for q in ['_later_queue1', '_later_queue2']:
2916 if not self._RunQueue(q):
2917 return
2918
2919 def _RunQueue(self, queue):
2920 for m in getattr(self, queue):
2921 if not m.Run(self):
David Rileye0684ad2017-04-05 00:02:59 -07002922 self._MarkUnclean()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002923 return False
2924 setattr(self, queue, [])
2925 return True
2926
2927 def _PrintMessages(self):
2928 for m in self._messages:
2929 m.Print(self)
2930 for m in self._failures:
2931 m.Print(self)
2932
2933 self._messages = []
2934 self._failures = []
2935
2936
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002937class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002938
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002939 """A special project housed under .repo.
2940 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002941
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002942 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002943 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002944 manifest=manifest,
2945 name=name,
2946 gitdir=gitdir,
2947 objdir=gitdir,
2948 worktree=worktree,
2949 remote=RemoteSpec('origin'),
2950 relpath='.repo/%s' % name,
2951 revisionExpr='refs/heads/master',
2952 revisionId=None,
2953 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002954
2955 def PreSync(self):
2956 if self.Exists:
2957 cb = self.CurrentBranch
2958 if cb:
2959 base = self.GetBranch(cb).merge
2960 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002961 self.revisionExpr = base
2962 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002963
Martin Kelly224a31a2017-07-10 14:46:25 -07002964 def MetaBranchSwitch(self, submodules=False):
Florian Vallee5d016502012-06-07 17:19:26 +02002965 """ Prepare MetaProject for manifest branch switch
2966 """
2967
2968 # detach and delete manifest branch, allowing a new
2969 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01002970 syncbuf = SyncBuffer(self.config, detach_head=True)
Martin Kelly224a31a2017-07-10 14:46:25 -07002971 self.Sync_LocalHalf(syncbuf, submodules=submodules)
Florian Vallee5d016502012-06-07 17:19:26 +02002972 syncbuf.Finish()
2973
2974 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002975 ['update-ref', '-d', 'refs/heads/default'],
2976 capture_stdout=True,
2977 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02002978
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002979 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07002980 def LastFetch(self):
2981 try:
2982 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
2983 return os.path.getmtime(fh)
2984 except OSError:
2985 return 0
2986
2987 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002988 def HasChanges(self):
2989 """Has the remote received new commits not yet checked out?
2990 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002991 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07002992 return False
2993
David Pursehouse8a68ff92012-09-24 12:15:13 +09002994 all_refs = self.bare_ref.all
2995 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07002996 head = self.work_git.GetHead()
2997 if head.startswith(R_HEADS):
2998 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09002999 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07003000 except KeyError:
3001 head = None
3002
3003 if revid == head:
3004 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07003005 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07003006 return True
3007 return False