blob: 45ecfe36187ea4309f4acf24c50a5ec8a7390f3d [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,
178 draft=False,
179 dest_branch=None):
Shawn O. Pearcec99883f2008-11-11 17:12:43 -0800180 self.project.UploadForReview(self.name,
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -0700181 people,
Brian Harring435370c2012-07-28 15:37:04 -0700182 auto_topic=auto_topic,
Bryan Jacobsf609f912013-05-06 13:36:24 -0400183 draft=draft,
184 dest_branch=dest_branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700185
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700186 def GetPublishedRefs(self):
187 refs = {}
188 output = self.project.bare_git.ls_remote(
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700189 self.branch.remote.SshReviewUrl(self.project.UserEmail),
190 'refs/changes/*')
Ficus Kirkpatrickbc7ef672009-05-04 12:45:11 -0700191 for line in output.split('\n'):
192 try:
193 (sha, ref) = line.split()
194 refs[sha] = ref
195 except ValueError:
196 pass
197
198 return refs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700199
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700200
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700201class StatusColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700202
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700203 def __init__(self, config):
204 Coloring.__init__(self, config, 'status')
Anthony King7bdac712014-07-16 12:56:40 +0100205 self.project = self.printer('header', attr='bold')
206 self.branch = self.printer('header', attr='bold')
207 self.nobranch = self.printer('nobranch', fg='red')
208 self.important = self.printer('important', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700209
Anthony King7bdac712014-07-16 12:56:40 +0100210 self.added = self.printer('added', fg='green')
211 self.changed = self.printer('changed', fg='red')
212 self.untracked = self.printer('untracked', fg='red')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700213
214
215class DiffColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700216
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700217 def __init__(self, config):
218 Coloring.__init__(self, config, 'diff')
Anthony King7bdac712014-07-16 12:56:40 +0100219 self.project = self.printer('header', attr='bold')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700220
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700221
Anthony King7bdac712014-07-16 12:56:40 +0100222class _Annotation(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700223
James W. Mills24c13082012-04-12 15:04:13 -0500224 def __init__(self, name, value, keep):
225 self.name = name
226 self.value = value
227 self.keep = keep
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700228
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700229
Anthony King7bdac712014-07-16 12:56:40 +0100230class _CopyFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700231
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800232 def __init__(self, src, dest, abssrc, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700233 self.src = src
234 self.dest = dest
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800235 self.abs_src = abssrc
236 self.abs_dest = absdest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700237
238 def _Copy(self):
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -0800239 src = self.abs_src
240 dest = self.abs_dest
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700241 # copy file if it does not exist or is out of date
242 if not os.path.exists(dest) or not filecmp.cmp(src, dest):
243 try:
244 # remove existing file first, since it might be read-only
245 if os.path.exists(dest):
246 os.remove(dest)
Matthew Buckett2daf6672009-07-11 09:43:47 -0400247 else:
Mickaël Salaün2f6ab7f2012-09-30 00:37:55 +0200248 dest_dir = os.path.dirname(dest)
249 if not os.path.isdir(dest_dir):
250 os.makedirs(dest_dir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700251 shutil.copy(src, dest)
252 # make the file read-only
253 mode = os.stat(dest)[stat.ST_MODE]
254 mode = mode & ~(stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH)
255 os.chmod(dest, mode)
256 except IOError:
Shawn O. Pearce48244782009-04-16 08:25:57 -0700257 _error('Cannot copy file %s to %s', src, dest)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700258
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700259
Anthony King7bdac712014-07-16 12:56:40 +0100260class _LinkFile(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700261
Wink Saville4c426ef2015-06-03 08:05:17 -0700262 def __init__(self, git_worktree, src, dest, relsrc, absdest):
263 self.git_worktree = git_worktree
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500264 self.src = src
265 self.dest = dest
Colin Cross0184dcc2015-05-05 00:24:54 -0700266 self.src_rel_to_dest = relsrc
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500267 self.abs_dest = absdest
268
Wink Saville4c426ef2015-06-03 08:05:17 -0700269 def __linkIt(self, relSrc, absDest):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500270 # link file if it does not exist or is out of date
Wink Saville4c426ef2015-06-03 08:05:17 -0700271 if not os.path.islink(absDest) or (os.readlink(absDest) != relSrc):
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500272 try:
273 # remove existing file first, since it might be read-only
Dan Willemsene1e0bd12015-11-18 16:49:38 -0800274 if os.path.lexists(absDest):
Wink Saville4c426ef2015-06-03 08:05:17 -0700275 os.remove(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500276 else:
Wink Saville4c426ef2015-06-03 08:05:17 -0700277 dest_dir = os.path.dirname(absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500278 if not os.path.isdir(dest_dir):
279 os.makedirs(dest_dir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700280 os.symlink(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500281 except IOError:
Wink Saville4c426ef2015-06-03 08:05:17 -0700282 _error('Cannot link file %s to %s', relSrc, absDest)
283
284 def _Link(self):
285 """Link the self.rel_src_to_dest and self.abs_dest. Handles wild cards
286 on the src linking all of the files in the source in to the destination
287 directory.
288 """
289 # We use the absSrc to handle the situation where the current directory
290 # is not the root of the repo
291 absSrc = os.path.join(self.git_worktree, self.src)
292 if os.path.exists(absSrc):
293 # Entity exists so just a simple one to one link operation
294 self.__linkIt(self.src_rel_to_dest, self.abs_dest)
295 else:
296 # Entity doesn't exist assume there is a wild card
297 absDestDir = self.abs_dest
298 if os.path.exists(absDestDir) and not os.path.isdir(absDestDir):
299 _error('Link error: src with wildcard, %s must be a directory',
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700300 absDestDir)
Wink Saville4c426ef2015-06-03 08:05:17 -0700301 else:
302 absSrcFiles = glob.glob(absSrc)
303 for absSrcFile in absSrcFiles:
304 # Create a releative path from source dir to destination dir
305 absSrcDir = os.path.dirname(absSrcFile)
306 relSrcDir = os.path.relpath(absSrcDir, absDestDir)
307
308 # Get the source file name
309 srcFile = os.path.basename(absSrcFile)
310
311 # Now form the final full paths to srcFile. They will be
312 # absolute for the desintaiton and relative for the srouce.
313 absDest = os.path.join(absDestDir, srcFile)
314 relSrc = os.path.join(relSrcDir, srcFile)
315 self.__linkIt(relSrc, absDest)
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500316
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700317
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700318class RemoteSpec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700319
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700320 def __init__(self,
321 name,
Anthony King7bdac712014-07-16 12:56:40 +0100322 url=None,
Steve Raed6480452016-08-10 15:00:00 -0700323 pushUrl=None,
Anthony King7bdac712014-07-16 12:56:40 +0100324 review=None,
Dan Willemsen96c2d652016-04-06 16:03:54 -0700325 revision=None,
326 orig_name=None):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700327 self.name = name
328 self.url = url
Steve Raed6480452016-08-10 15:00:00 -0700329 self.pushUrl = pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -0700330 self.review = review
Anthony King36ea2fb2014-05-06 11:54:01 +0100331 self.revision = revision
Dan Willemsen96c2d652016-04-06 16:03:54 -0700332 self.orig_name = orig_name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700333
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700334
Doug Anderson37282b42011-03-04 11:54:18 -0800335class RepoHook(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700336
Doug Anderson37282b42011-03-04 11:54:18 -0800337 """A RepoHook contains information about a script to run as a hook.
338
339 Hooks are used to run a python script before running an upload (for instance,
340 to run presubmit checks). Eventually, we may have hooks for other actions.
341
342 This shouldn't be confused with files in the 'repo/hooks' directory. Those
343 files are copied into each '.git/hooks' folder for each project. Repo-level
344 hooks are associated instead with repo actions.
345
346 Hooks are always python. When a hook is run, we will load the hook into the
347 interpreter and execute its main() function.
348 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700349
Doug Anderson37282b42011-03-04 11:54:18 -0800350 def __init__(self,
351 hook_type,
352 hooks_project,
353 topdir,
Mike Frysinger40252c22016-08-15 21:23:44 -0400354 manifest_url,
Doug Anderson37282b42011-03-04 11:54:18 -0800355 abort_if_user_denies=False):
356 """RepoHook constructor.
357
358 Params:
359 hook_type: A string representing the type of hook. This is also used
360 to figure out the name of the file containing the hook. For
361 example: 'pre-upload'.
362 hooks_project: The project containing the repo hooks. If you have a
363 manifest, this is manifest.repo_hooks_project. OK if this is None,
364 which will make the hook a no-op.
365 topdir: Repo's top directory (the one containing the .repo directory).
366 Scripts will run with CWD as this directory. If you have a manifest,
367 this is manifest.topdir
Mike Frysinger40252c22016-08-15 21:23:44 -0400368 manifest_url: The URL to the manifest git repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800369 abort_if_user_denies: If True, we'll throw a HookError() if the user
370 doesn't allow us to run the hook.
371 """
372 self._hook_type = hook_type
373 self._hooks_project = hooks_project
Mike Frysinger40252c22016-08-15 21:23:44 -0400374 self._manifest_url = manifest_url
Doug Anderson37282b42011-03-04 11:54:18 -0800375 self._topdir = topdir
376 self._abort_if_user_denies = abort_if_user_denies
377
378 # Store the full path to the script for convenience.
379 if self._hooks_project:
380 self._script_fullpath = os.path.join(self._hooks_project.worktree,
381 self._hook_type + '.py')
382 else:
383 self._script_fullpath = None
384
385 def _GetHash(self):
386 """Return a hash of the contents of the hooks directory.
387
388 We'll just use git to do this. This hash has the property that if anything
389 changes in the directory we will return a different has.
390
391 SECURITY CONSIDERATION:
392 This hash only represents the contents of files in the hook directory, not
393 any other files imported or called by hooks. Changes to imported files
394 can change the script behavior without affecting the hash.
395
396 Returns:
397 A string representing the hash. This will always be ASCII so that it can
398 be printed to the user easily.
399 """
400 assert self._hooks_project, "Must have hooks to calculate their hash."
401
402 # We will use the work_git object rather than just calling GetRevisionId().
403 # That gives us a hash of the latest checked in version of the files that
404 # the user will actually be executing. Specifically, GetRevisionId()
405 # doesn't appear to change even if a user checks out a different version
406 # of the hooks repo (via git checkout) nor if a user commits their own revs.
407 #
408 # NOTE: Local (non-committed) changes will not be factored into this hash.
409 # I think this is OK, since we're really only worried about warning the user
410 # about upstream changes.
411 return self._hooks_project.work_git.rev_parse('HEAD')
412
413 def _GetMustVerb(self):
414 """Return 'must' if the hook is required; 'should' if not."""
415 if self._abort_if_user_denies:
416 return 'must'
417 else:
418 return 'should'
419
420 def _CheckForHookApproval(self):
421 """Check to see whether this hook has been approved.
422
Mike Frysinger40252c22016-08-15 21:23:44 -0400423 We'll accept approval of manifest URLs if they're using secure transports.
424 This way the user can say they trust the manifest hoster. For insecure
425 hosts, we fall back to checking the hash of the hooks repo.
Doug Anderson37282b42011-03-04 11:54:18 -0800426
427 Note that we ask permission for each individual hook even though we use
428 the hash of all hooks when detecting changes. We'd like the user to be
429 able to approve / deny each hook individually. We only use the hash of all
430 hooks because there is no other easy way to detect changes to local imports.
431
432 Returns:
433 True if this hook is approved to run; False otherwise.
434
435 Raises:
436 HookError: Raised if the user doesn't approve and abort_if_user_denies
437 was passed to the consturctor.
438 """
Mike Frysinger40252c22016-08-15 21:23:44 -0400439 if self._ManifestUrlHasSecureScheme():
440 return self._CheckForHookApprovalManifest()
441 else:
442 return self._CheckForHookApprovalHash()
443
444 def _CheckForHookApprovalHelper(self, subkey, new_val, main_prompt,
445 changed_prompt):
446 """Check for approval for a particular attribute and hook.
447
448 Args:
449 subkey: The git config key under [repo.hooks.<hook_type>] to store the
450 last approved string.
451 new_val: The new value to compare against the last approved one.
452 main_prompt: Message to display to the user to ask for approval.
453 changed_prompt: Message explaining why we're re-asking for approval.
454
455 Returns:
456 True if this hook is approved to run; False otherwise.
Doug Anderson37282b42011-03-04 11:54:18 -0800457
Mike Frysinger40252c22016-08-15 21:23:44 -0400458 Raises:
459 HookError: Raised if the user doesn't approve and abort_if_user_denies
460 was passed to the consturctor.
461 """
462 hooks_config = self._hooks_project.config
463 git_approval_key = 'repo.hooks.%s.%s' % (self._hook_type, subkey)
Doug Anderson37282b42011-03-04 11:54:18 -0800464
Mike Frysinger40252c22016-08-15 21:23:44 -0400465 # Get the last value that the user approved for this hook; may be None.
466 old_val = hooks_config.GetString(git_approval_key)
Doug Anderson37282b42011-03-04 11:54:18 -0800467
Mike Frysinger40252c22016-08-15 21:23:44 -0400468 if old_val is not None:
Doug Anderson37282b42011-03-04 11:54:18 -0800469 # User previously approved hook and asked not to be prompted again.
Mike Frysinger40252c22016-08-15 21:23:44 -0400470 if new_val == old_val:
Doug Anderson37282b42011-03-04 11:54:18 -0800471 # Approval matched. We're done.
472 return True
473 else:
474 # Give the user a reason why we're prompting, since they last told
475 # us to "never ask again".
Mike Frysinger40252c22016-08-15 21:23:44 -0400476 prompt = 'WARNING: %s\n\n' % (changed_prompt,)
Doug Anderson37282b42011-03-04 11:54:18 -0800477 else:
478 prompt = ''
479
480 # Prompt the user if we're not on a tty; on a tty we'll assume "no".
481 if sys.stdout.isatty():
Mike Frysinger40252c22016-08-15 21:23:44 -0400482 prompt += main_prompt + ' (yes/always/NO)? '
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530483 response = input(prompt).lower()
David Pursehouse98ffba12012-11-14 11:18:00 +0900484 print()
Doug Anderson37282b42011-03-04 11:54:18 -0800485
486 # User is doing a one-time approval.
487 if response in ('y', 'yes'):
488 return True
Mike Frysinger40252c22016-08-15 21:23:44 -0400489 elif response == 'always':
490 hooks_config.SetString(git_approval_key, new_val)
Doug Anderson37282b42011-03-04 11:54:18 -0800491 return True
492
493 # For anything else, we'll assume no approval.
494 if self._abort_if_user_denies:
495 raise HookError('You must allow the %s hook or use --no-verify.' %
496 self._hook_type)
497
498 return False
499
Mike Frysinger40252c22016-08-15 21:23:44 -0400500 def _ManifestUrlHasSecureScheme(self):
501 """Check if the URI for the manifest is a secure transport."""
502 secure_schemes = ('file', 'https', 'ssh', 'persistent-https', 'sso', 'rpc')
503 parse_results = urllib.parse.urlparse(self._manifest_url)
504 return parse_results.scheme in secure_schemes
505
506 def _CheckForHookApprovalManifest(self):
507 """Check whether the user has approved this manifest host.
508
509 Returns:
510 True if this hook is approved to run; False otherwise.
511 """
512 return self._CheckForHookApprovalHelper(
513 'approvedmanifest',
514 self._manifest_url,
515 'Run hook scripts from %s' % (self._manifest_url,),
516 'Manifest URL has changed since %s was allowed.' % (self._hook_type,))
517
518 def _CheckForHookApprovalHash(self):
519 """Check whether the user has approved the hooks repo.
520
521 Returns:
522 True if this hook is approved to run; False otherwise.
523 """
524 prompt = ('Repo %s run the script:\n'
525 ' %s\n'
526 '\n'
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700527 'Do you want to allow this script to run')
Mike Frysinger40252c22016-08-15 21:23:44 -0400528 return self._CheckForHookApprovalHelper(
529 'approvedhash',
530 self._GetHash(),
Jonathan Nieder71e4cea2016-08-16 12:05:09 -0700531 prompt % (self._GetMustVerb(), self._script_fullpath),
Mike Frysinger40252c22016-08-15 21:23:44 -0400532 'Scripts have changed since %s was allowed.' % (self._hook_type,))
533
Doug Anderson37282b42011-03-04 11:54:18 -0800534 def _ExecuteHook(self, **kwargs):
535 """Actually execute the given hook.
536
537 This will run the hook's 'main' function in our python interpreter.
538
539 Args:
540 kwargs: Keyword arguments to pass to the hook. These are often specific
541 to the hook type. For instance, pre-upload hooks will contain
542 a project_list.
543 """
544 # Keep sys.path and CWD stashed away so that we can always restore them
545 # upon function exit.
546 orig_path = os.getcwd()
547 orig_syspath = sys.path
548
549 try:
550 # Always run hooks with CWD as topdir.
551 os.chdir(self._topdir)
552
553 # Put the hook dir as the first item of sys.path so hooks can do
554 # relative imports. We want to replace the repo dir as [0] so
555 # hooks can't import repo files.
556 sys.path = [os.path.dirname(self._script_fullpath)] + sys.path[1:]
557
558 # Exec, storing global context in the context dict. We catch exceptions
559 # and convert to a HookError w/ just the failing traceback.
Mike Frysinger4aa4b212016-03-04 15:03:00 -0500560 context = {'__file__': self._script_fullpath}
Doug Anderson37282b42011-03-04 11:54:18 -0800561 try:
Anthony King70f68902014-05-05 21:15:34 +0100562 exec(compile(open(self._script_fullpath).read(),
563 self._script_fullpath, 'exec'), context)
Doug Anderson37282b42011-03-04 11:54:18 -0800564 except Exception:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700565 raise HookError('%s\nFailed to import %s hook; see traceback above.' %
566 (traceback.format_exc(), self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800567
568 # Running the script should have defined a main() function.
569 if 'main' not in context:
570 raise HookError('Missing main() in: "%s"' % self._script_fullpath)
571
Doug Anderson37282b42011-03-04 11:54:18 -0800572 # Add 'hook_should_take_kwargs' to the arguments to be passed to main.
573 # We don't actually want hooks to define their main with this argument--
574 # it's there to remind them that their hook should always take **kwargs.
575 # For instance, a pre-upload hook should be defined like:
576 # def main(project_list, **kwargs):
577 #
578 # This allows us to later expand the API without breaking old hooks.
579 kwargs = kwargs.copy()
580 kwargs['hook_should_take_kwargs'] = True
581
582 # Call the main function in the hook. If the hook should cause the
583 # build to fail, it will raise an Exception. We'll catch that convert
584 # to a HookError w/ just the failing traceback.
585 try:
586 context['main'](**kwargs)
587 except Exception:
588 raise HookError('%s\nFailed to run main() for %s hook; see traceback '
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700589 'above.' % (traceback.format_exc(),
590 self._hook_type))
Doug Anderson37282b42011-03-04 11:54:18 -0800591 finally:
592 # Restore sys.path and CWD.
593 sys.path = orig_syspath
594 os.chdir(orig_path)
595
596 def Run(self, user_allows_all_hooks, **kwargs):
597 """Run the hook.
598
599 If the hook doesn't exist (because there is no hooks project or because
600 this particular hook is not enabled), this is a no-op.
601
602 Args:
603 user_allows_all_hooks: If True, we will never prompt about running the
604 hook--we'll just assume it's OK to run it.
605 kwargs: Keyword arguments to pass to the hook. These are often specific
606 to the hook type. For instance, pre-upload hooks will contain
607 a project_list.
608
609 Raises:
610 HookError: If there was a problem finding the hook or the user declined
611 to run a required hook (from _CheckForHookApproval).
612 """
613 # No-op if there is no hooks project or if hook is disabled.
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700614 if ((not self._hooks_project) or (self._hook_type not in
615 self._hooks_project.enabled_repo_hooks)):
Doug Anderson37282b42011-03-04 11:54:18 -0800616 return
617
618 # Bail with a nice error if we can't find the hook.
619 if not os.path.isfile(self._script_fullpath):
620 raise HookError('Couldn\'t find repo hook: "%s"' % self._script_fullpath)
621
622 # Make sure the user is OK with running the hook.
623 if (not user_allows_all_hooks) and (not self._CheckForHookApproval()):
624 return
625
626 # Run the hook with the same version of python we're using.
627 self._ExecuteHook(**kwargs)
628
629
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700630class Project(object):
Kevin Degi384b3c52014-10-16 16:02:58 -0600631 # These objects can be shared between several working trees.
632 shareable_files = ['description', 'info']
633 shareable_dirs = ['hooks', 'objects', 'rr-cache', 'svn']
634 # These objects can only be used by a single working tree.
635 working_tree_files = ['config', 'packed-refs', 'shallow']
636 working_tree_dirs = ['logs', 'refs']
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700637
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700638 def __init__(self,
639 manifest,
640 name,
641 remote,
642 gitdir,
David James8d201162013-10-11 17:03:19 -0700643 objdir,
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700644 worktree,
645 relpath,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700646 revisionExpr,
Mike Pontillod3153822012-02-28 11:53:24 -0800647 revisionId,
Anthony King7bdac712014-07-16 12:56:40 +0100648 rebase=True,
649 groups=None,
650 sync_c=False,
651 sync_s=False,
652 clone_depth=None,
653 upstream=None,
654 parent=None,
655 is_derived=False,
David Pursehouseb1553542014-09-04 21:28:09 +0900656 dest_branch=None,
Simran Basib9a1b732015-08-20 12:19:28 -0700657 optimized_fetch=False,
658 old_revision=None):
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800659 """Init a Project object.
660
661 Args:
662 manifest: The XmlManifest object.
663 name: The `name` attribute of manifest.xml's project element.
664 remote: RemoteSpec object specifying its remote's properties.
665 gitdir: Absolute path of git directory.
David James8d201162013-10-11 17:03:19 -0700666 objdir: Absolute path of directory to store git objects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800667 worktree: Absolute path of git working tree.
668 relpath: Relative path of git working tree to repo's top directory.
669 revisionExpr: The `revision` attribute of manifest.xml's project element.
670 revisionId: git commit id for checking out.
671 rebase: The `rebase` attribute of manifest.xml's project element.
672 groups: The `groups` attribute of manifest.xml's project element.
673 sync_c: The `sync-c` attribute of manifest.xml's project element.
674 sync_s: The `sync-s` attribute of manifest.xml's project element.
675 upstream: The `upstream` attribute of manifest.xml's project element.
676 parent: The parent Project object.
677 is_derived: False if the project was explicitly defined in the manifest;
678 True if the project is a discovered submodule.
Bryan Jacobsf609f912013-05-06 13:36:24 -0400679 dest_branch: The branch to which to push changes for review by default.
David Pursehouseb1553542014-09-04 21:28:09 +0900680 optimized_fetch: If True, when a project is set to a sha1 revision, only
681 fetch from the remote if the sha1 is not present locally.
Simran Basib9a1b732015-08-20 12:19:28 -0700682 old_revision: saved git commit id for open GITC projects.
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800683 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700684 self.manifest = manifest
685 self.name = name
686 self.remote = remote
Anthony Newnamdf14a702011-01-09 17:31:57 -0800687 self.gitdir = gitdir.replace('\\', '/')
David James8d201162013-10-11 17:03:19 -0700688 self.objdir = objdir.replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800689 if worktree:
Renaud Paquayfef9f212016-11-01 18:28:01 -0700690 self.worktree = os.path.normpath(worktree).replace('\\', '/')
Shawn O. Pearce0ce6ca92011-01-10 13:26:01 -0800691 else:
692 self.worktree = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700693 self.relpath = relpath
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700694 self.revisionExpr = revisionExpr
695
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700696 if revisionId is None \
697 and revisionExpr \
698 and IsId(revisionExpr):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -0700699 self.revisionId = revisionExpr
700 else:
701 self.revisionId = revisionId
702
Mike Pontillod3153822012-02-28 11:53:24 -0800703 self.rebase = rebase
Colin Cross5acde752012-03-28 20:15:45 -0700704 self.groups = groups
Anatol Pomazau79770d22012-04-20 14:41:59 -0700705 self.sync_c = sync_c
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800706 self.sync_s = sync_s
David Pursehouseede7f122012-11-27 22:25:30 +0900707 self.clone_depth = clone_depth
Brian Harring14a66742012-09-28 20:21:57 -0700708 self.upstream = upstream
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800709 self.parent = parent
710 self.is_derived = is_derived
David Pursehouseb1553542014-09-04 21:28:09 +0900711 self.optimized_fetch = optimized_fetch
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800712 self.subprojects = []
Mike Pontillod3153822012-02-28 11:53:24 -0800713
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700714 self.snapshots = {}
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700715 self.copyfiles = []
Jeff Hamiltone0df2322014-04-21 17:10:59 -0500716 self.linkfiles = []
James W. Mills24c13082012-04-12 15:04:13 -0500717 self.annotations = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700718 self.config = GitConfig.ForRepository(gitdir=self.gitdir,
719 defaults=self.manifest.globalConfig)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700720
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800721 if self.worktree:
David James8d201162013-10-11 17:03:19 -0700722 self.work_git = self._GitGetByExec(self, bare=False, gitdir=gitdir)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -0800723 else:
724 self.work_git = None
David James8d201162013-10-11 17:03:19 -0700725 self.bare_git = self._GitGetByExec(self, bare=True, gitdir=gitdir)
Shawn O. Pearced237b692009-04-17 18:49:50 -0700726 self.bare_ref = GitRefs(gitdir)
David James8d201162013-10-11 17:03:19 -0700727 self.bare_objdir = self._GitGetByExec(self, bare=True, gitdir=objdir)
Bryan Jacobsf609f912013-05-06 13:36:24 -0400728 self.dest_branch = dest_branch
Simran Basib9a1b732015-08-20 12:19:28 -0700729 self.old_revision = old_revision
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700730
Doug Anderson37282b42011-03-04 11:54:18 -0800731 # This will be filled in if a project is later identified to be the
732 # project containing repo hooks.
733 self.enabled_repo_hooks = []
734
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700735 @property
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +0800736 def Derived(self):
737 return self.is_derived
738
739 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700740 def Exists(self):
Kevin Degi384b3c52014-10-16 16:02:58 -0600741 return os.path.isdir(self.gitdir) and os.path.isdir(self.objdir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700742
743 @property
744 def CurrentBranch(self):
745 """Obtain the name of the currently checked out branch.
746 The branch name omits the 'refs/heads/' prefix.
747 None is returned if the project is on a detached HEAD.
748 """
Shawn O. Pearce5b23f242009-04-17 18:43:33 -0700749 b = self.work_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700750 if b.startswith(R_HEADS):
751 return b[len(R_HEADS):]
752 return None
753
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700754 def IsRebaseInProgress(self):
755 w = self.worktree
756 g = os.path.join(w, '.git')
757 return os.path.exists(os.path.join(g, 'rebase-apply')) \
758 or os.path.exists(os.path.join(g, 'rebase-merge')) \
759 or os.path.exists(os.path.join(w, '.dotest'))
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200760
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700761 def IsDirty(self, consider_untracked=True):
762 """Is the working directory modified in some way?
763 """
764 self.work_git.update_index('-q',
765 '--unmerged',
766 '--ignore-missing',
767 '--refresh')
David Pursehouse8f62fb72012-11-14 12:09:38 +0900768 if self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700769 return True
770 if self.work_git.DiffZ('diff-files'):
771 return True
772 if consider_untracked and self.work_git.LsOthers():
773 return True
774 return False
775
776 _userident_name = None
777 _userident_email = None
778
779 @property
780 def UserName(self):
781 """Obtain the user's personal name.
782 """
783 if self._userident_name is None:
784 self._LoadUserIdentity()
785 return self._userident_name
786
787 @property
788 def UserEmail(self):
789 """Obtain the user's email address. This is very likely
790 to be their Gerrit login.
791 """
792 if self._userident_email is None:
793 self._LoadUserIdentity()
794 return self._userident_email
795
796 def _LoadUserIdentity(self):
David Pursehousec1b86a22012-11-14 11:36:51 +0900797 u = self.bare_git.var('GIT_COMMITTER_IDENT')
798 m = re.compile("^(.*) <([^>]*)> ").match(u)
799 if m:
800 self._userident_name = m.group(1)
801 self._userident_email = m.group(2)
802 else:
803 self._userident_name = ''
804 self._userident_email = ''
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700805
806 def GetRemote(self, name):
807 """Get the configuration for a single remote.
808 """
809 return self.config.GetRemote(name)
810
811 def GetBranch(self, name):
812 """Get the configuration for a single branch.
813 """
814 return self.config.GetBranch(name)
815
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700816 def GetBranches(self):
817 """Get all existing local branches.
818 """
819 current = self.CurrentBranch
David Pursehouse8a68ff92012-09-24 12:15:13 +0900820 all_refs = self._allrefs
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700821 heads = {}
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700822
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530823 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700824 if name.startswith(R_HEADS):
825 name = name[len(R_HEADS):]
826 b = self.GetBranch(name)
827 b.current = name == current
828 b.published = None
David Pursehouse8a68ff92012-09-24 12:15:13 +0900829 b.revision = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700830 heads[name] = b
831
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530832 for name, ref_id in all_refs.items():
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700833 if name.startswith(R_PUB):
834 name = name[len(R_PUB):]
835 b = heads.get(name)
836 if b:
David Pursehouse8a68ff92012-09-24 12:15:13 +0900837 b.published = ref_id
Shawn O. Pearce27b07322009-04-10 16:02:48 -0700838
839 return heads
840
Colin Cross5acde752012-03-28 20:15:45 -0700841 def MatchesGroups(self, manifest_groups):
842 """Returns true if the manifest groups specified at init should cause
843 this project to be synced.
844 Prefixing a manifest group with "-" inverts the meaning of a group.
Conley Owensbb1b5f52012-08-13 13:11:18 -0700845 All projects are implicitly labelled with "all".
Colin Cross5acde752012-03-28 20:15:45 -0700846
Conley Owens971de8e2012-04-16 10:36:08 -0700847 labels are resolved in order. In the example case of
Conley Owensbb1b5f52012-08-13 13:11:18 -0700848 project_groups: "all,group1,group2"
Conley Owens971de8e2012-04-16 10:36:08 -0700849 manifest_groups: "-group1,group2"
850 the project will be matched.
David Holmer0a1c6a12012-11-14 19:19:00 -0500851
852 The special manifest group "default" will match any project that
853 does not have the special project group "notdefault"
Conley Owens971de8e2012-04-16 10:36:08 -0700854 """
David Holmer0a1c6a12012-11-14 19:19:00 -0500855 expanded_manifest_groups = manifest_groups or ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700856 expanded_project_groups = ['all'] + (self.groups or [])
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700857 if 'notdefault' not in expanded_project_groups:
David Holmer0a1c6a12012-11-14 19:19:00 -0500858 expanded_project_groups += ['default']
Conley Owensbb1b5f52012-08-13 13:11:18 -0700859
Conley Owens971de8e2012-04-16 10:36:08 -0700860 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700861 for group in expanded_manifest_groups:
862 if group.startswith('-') and group[1:] in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700863 matched = False
Conley Owensbb1b5f52012-08-13 13:11:18 -0700864 elif group in expanded_project_groups:
Conley Owens971de8e2012-04-16 10:36:08 -0700865 matched = True
Colin Cross5acde752012-03-28 20:15:45 -0700866
Conley Owens971de8e2012-04-16 10:36:08 -0700867 return matched
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700868
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700869# Status Display ##
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700870 def UncommitedFiles(self, get_all=True):
871 """Returns a list of strings, uncommitted files in the git tree.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700872
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700873 Args:
874 get_all: a boolean, if True - get information about all different
875 uncommitted files. If False - return as soon as any kind of
876 uncommitted files is detected.
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500877 """
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700878 details = []
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500879 self.work_git.update_index('-q',
880 '--unmerged',
881 '--ignore-missing',
882 '--refresh')
883 if self.IsRebaseInProgress():
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700884 details.append("rebase in progress")
885 if not get_all:
886 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500887
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700888 changes = self.work_git.DiffZ('diff-index', '--cached', HEAD).keys()
889 if changes:
890 details.extend(changes)
891 if not get_all:
892 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500893
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700894 changes = self.work_git.DiffZ('diff-files').keys()
895 if changes:
896 details.extend(changes)
897 if not get_all:
898 return details
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500899
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700900 changes = self.work_git.LsOthers()
901 if changes:
902 details.extend(changes)
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500903
Vadim Bendebury14e134d2014-10-05 15:40:30 -0700904 return details
905
906 def HasChanges(self):
907 """Returns true if there are uncommitted changes.
908 """
909 if self.UncommitedFiles(get_all=False):
910 return True
911 else:
912 return False
Anthony Newnamcc50bac2010-04-08 10:28:59 -0500913
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600914 def PrintWorkTreeStatus(self, output_redir=None, quiet=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700915 """Prints the status of the repository to stdout.
Terence Haddock4655e812011-03-31 12:33:34 +0200916
917 Args:
918 output: If specified, redirect the output to this object.
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600919 quiet: If True then only print the project name. Do not print
920 the modified files, branch name, etc.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700921 """
922 if not os.path.isdir(self.worktree):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700923 if output_redir is None:
Terence Haddock4655e812011-03-31 12:33:34 +0200924 output_redir = sys.stdout
Sarah Owenscecd1d82012-11-01 22:59:27 -0700925 print(file=output_redir)
926 print('project %s/' % self.relpath, file=output_redir)
927 print(' missing (run "repo sync")', file=output_redir)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700928 return
929
930 self.work_git.update_index('-q',
931 '--unmerged',
932 '--ignore-missing',
933 '--refresh')
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700934 rb = self.IsRebaseInProgress()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700935 di = self.work_git.DiffZ('diff-index', '-M', '--cached', HEAD)
936 df = self.work_git.DiffZ('diff-files')
937 do = self.work_git.LsOthers()
Ali Utku Selen76abcc12012-01-25 10:51:12 +0100938 if not rb and not di and not df and not do and not self.CurrentBranch:
Shawn O. Pearce161f4452009-04-10 17:41:44 -0700939 return 'CLEAN'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700940
941 out = StatusColoring(self.config)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700942 if output_redir is not None:
Terence Haddock4655e812011-03-31 12:33:34 +0200943 out.redirect(output_redir)
Jakub Vrana0402cd82014-09-09 15:39:15 -0700944 out.project('project %-40s', self.relpath + '/ ')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700945
Andrew Wheeler4d5bb682012-02-27 13:52:22 -0600946 if quiet:
947 out.nl()
948 return 'DIRTY'
949
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700950 branch = self.CurrentBranch
951 if branch is None:
952 out.nobranch('(*** NO BRANCH ***)')
953 else:
954 out.branch('branch %s', branch)
955 out.nl()
956
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -0700957 if rb:
958 out.important('prior sync failed; rebase still in progress')
959 out.nl()
960
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700961 paths = list()
962 paths.extend(di.keys())
963 paths.extend(df.keys())
964 paths.extend(do)
965
Chirayu Desai217ea7d2013-03-01 19:14:38 +0530966 for p in sorted(set(paths)):
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900967 try:
968 i = di[p]
969 except KeyError:
970 i = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700971
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900972 try:
973 f = df[p]
974 except KeyError:
975 f = None
Julius Gustavsson0cb1b3f2010-06-17 17:55:02 +0200976
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900977 if i:
978 i_status = i.status.upper()
979 else:
980 i_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700981
David Pursehouse5c6eeac2012-10-11 16:44:48 +0900982 if f:
983 f_status = f.status.lower()
984 else:
985 f_status = '-'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700986
987 if i and i.src_path:
Shawn O. Pearcefe086752009-03-03 13:49:48 -0800988 line = ' %s%s\t%s => %s (%s%%)' % (i_status, f_status,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -0700989 i.src_path, p, i.level)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -0700990 else:
991 line = ' %s%s\t%s' % (i_status, f_status, p)
992
993 if i and not f:
994 out.added('%s', line)
995 elif (i and f) or (not i and f):
996 out.changed('%s', line)
997 elif not i and not f:
998 out.untracked('%s', line)
999 else:
1000 out.write('%s', line)
1001 out.nl()
Terence Haddock4655e812011-03-31 12:33:34 +02001002
Shawn O. Pearce161f4452009-04-10 17:41:44 -07001003 return 'DIRTY'
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001004
pelyad67872d2012-03-28 14:49:58 +03001005 def PrintWorkTreeDiff(self, absolute_paths=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001006 """Prints the status of the repository to stdout.
1007 """
1008 out = DiffColoring(self.config)
1009 cmd = ['diff']
1010 if out.is_on:
1011 cmd.append('--color')
1012 cmd.append(HEAD)
pelyad67872d2012-03-28 14:49:58 +03001013 if absolute_paths:
1014 cmd.append('--src-prefix=a/%s/' % self.relpath)
1015 cmd.append('--dst-prefix=b/%s/' % self.relpath)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001016 cmd.append('--')
1017 p = GitCommand(self,
1018 cmd,
Anthony King7bdac712014-07-16 12:56:40 +01001019 capture_stdout=True,
1020 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001021 has_diff = False
1022 for line in p.process.stdout:
1023 if not has_diff:
1024 out.nl()
1025 out.project('project %s/' % self.relpath)
1026 out.nl()
1027 has_diff = True
Sarah Owenscecd1d82012-11-01 22:59:27 -07001028 print(line[:-1])
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001029 p.Wait()
1030
1031
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001032# Publish / Upload ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001033
David Pursehouse8a68ff92012-09-24 12:15:13 +09001034 def WasPublished(self, branch, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001035 """Was the branch published (uploaded) for code review?
1036 If so, returns the SHA-1 hash of the last published
1037 state for the branch.
1038 """
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001039 key = R_PUB + branch
David Pursehouse8a68ff92012-09-24 12:15:13 +09001040 if all_refs is None:
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001041 try:
1042 return self.bare_git.rev_parse(key)
1043 except GitError:
1044 return None
1045 else:
1046 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001047 return all_refs[key]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001048 except KeyError:
1049 return None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001050
David Pursehouse8a68ff92012-09-24 12:15:13 +09001051 def CleanPublishedCache(self, all_refs=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001052 """Prunes any stale published refs.
1053 """
David Pursehouse8a68ff92012-09-24 12:15:13 +09001054 if all_refs is None:
1055 all_refs = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001056 heads = set()
1057 canrm = {}
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301058 for name, ref_id in all_refs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001059 if name.startswith(R_HEADS):
1060 heads.add(name)
1061 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001062 canrm[name] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001063
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301064 for name, ref_id in canrm.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001065 n = name[len(R_PUB):]
1066 if R_HEADS + n not in heads:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001067 self.bare_git.DeleteRef(name, ref_id)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001068
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001069 def GetUploadableBranches(self, selected_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001070 """List any branches which can be uploaded for review.
1071 """
1072 heads = {}
1073 pubed = {}
1074
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301075 for name, ref_id in self._allrefs.items():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001076 if name.startswith(R_HEADS):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001077 heads[name[len(R_HEADS):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001078 elif name.startswith(R_PUB):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001079 pubed[name[len(R_PUB):]] = ref_id
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001080
1081 ready = []
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301082 for branch, ref_id in heads.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001083 if branch in pubed and pubed[branch] == ref_id:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001084 continue
Mandeep Singh Bainesd6c93a22011-05-26 10:34:11 -07001085 if selected_branch and branch != selected_branch:
1086 continue
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001087
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001088 rb = self.GetUploadableBranch(branch)
1089 if rb:
1090 ready.append(rb)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001091 return ready
1092
Shawn O. Pearce35f25962008-11-11 17:03:13 -08001093 def GetUploadableBranch(self, branch_name):
1094 """Get a single uploadable branch, or None.
1095 """
1096 branch = self.GetBranch(branch_name)
1097 base = branch.LocalMerge
1098 if branch.LocalMerge:
1099 rb = ReviewableBranch(self, branch, base)
1100 if rb.commits:
1101 return rb
1102 return None
1103
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001104 def UploadForReview(self, branch=None,
Anthony King7bdac712014-07-16 12:56:40 +01001105 people=([], []),
Brian Harring435370c2012-07-28 15:37:04 -07001106 auto_topic=False,
Bryan Jacobsf609f912013-05-06 13:36:24 -04001107 draft=False,
1108 dest_branch=None):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001109 """Uploads the named branch for code review.
1110 """
1111 if branch is None:
1112 branch = self.CurrentBranch
1113 if branch is None:
1114 raise GitError('not currently on a branch')
1115
1116 branch = self.GetBranch(branch)
1117 if not branch.LocalMerge:
1118 raise GitError('branch %s does not track a remote' % branch.name)
1119 if not branch.remote.review:
1120 raise GitError('remote %s has no review url' % branch.remote.name)
1121
Bryan Jacobsf609f912013-05-06 13:36:24 -04001122 if dest_branch is None:
1123 dest_branch = self.dest_branch
1124 if dest_branch is None:
1125 dest_branch = branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001126 if not dest_branch.startswith(R_HEADS):
1127 dest_branch = R_HEADS + dest_branch
1128
Shawn O. Pearce339ba9f2008-11-06 09:52:51 -08001129 if not branch.remote.projectname:
1130 branch.remote.projectname = self.name
1131 branch.remote.Save()
1132
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001133 url = branch.remote.ReviewUrl(self.UserEmail)
1134 if url is None:
1135 raise UploadError('review not configured')
1136 cmd = ['push']
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001137
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001138 if url.startswith('ssh://'):
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001139 rp = ['gerrit receive-pack']
1140 for e in people[0]:
1141 rp.append('--reviewer=%s' % sq(e))
1142 for e in people[1]:
1143 rp.append('--cc=%s' % sq(e))
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001144 cmd.append('--receive-pack=%s' % " ".join(rp))
Shawn O. Pearcea5ece0e2010-07-15 16:52:42 -07001145
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001146 cmd.append(url)
1147
1148 if dest_branch.startswith(R_HEADS):
1149 dest_branch = dest_branch[len(R_HEADS):]
Brian Harring435370c2012-07-28 15:37:04 -07001150
1151 upload_type = 'for'
1152 if draft:
1153 upload_type = 'drafts'
1154
1155 ref_spec = '%s:refs/%s/%s' % (R_HEADS + branch.name, upload_type,
1156 dest_branch)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001157 if auto_topic:
1158 ref_spec = ref_spec + '/' + branch.name
Shawn Pearce45d21682013-02-28 00:35:51 -08001159 if not url.startswith('ssh://'):
1160 rp = ['r=%s' % p for p in people[0]] + \
1161 ['cc=%s' % p for p in people[1]]
1162 if rp:
1163 ref_spec = ref_spec + '%' + ','.join(rp)
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001164 cmd.append(ref_spec)
Shawn O. Pearceb54a3922009-01-05 16:18:58 -08001165
Anthony King7bdac712014-07-16 12:56:40 +01001166 if GitCommand(self, cmd, bare=True).Wait() != 0:
Shawn O. Pearcec9571422012-01-11 14:58:54 -08001167 raise UploadError('Upload failed')
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001168
1169 msg = "posted to %s for %s" % (branch.remote.review, dest_branch)
1170 self.bare_git.UpdateRef(R_PUB + branch.name,
1171 R_HEADS + branch.name,
Anthony King7bdac712014-07-16 12:56:40 +01001172 message=msg)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001173
1174
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001175# Sync ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001176
Julien Campergue335f5ef2013-10-16 11:02:35 +02001177 def _ExtractArchive(self, tarpath, path=None):
1178 """Extract the given tar on its current location
1179
1180 Args:
1181 - tarpath: The path to the actual tar file
1182
1183 """
1184 try:
1185 with tarfile.open(tarpath, 'r') as tar:
1186 tar.extractall(path=path)
1187 return True
1188 except (IOError, tarfile.TarError) as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001189 _error("Cannot extract archive %s: %s", tarpath, str(e))
Julien Campergue335f5ef2013-10-16 11:02:35 +02001190 return False
1191
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001192 def Sync_NetworkHalf(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001193 quiet=False,
1194 is_new=None,
1195 current_branch_only=False,
1196 force_sync=False,
1197 clone_bundle=True,
1198 no_tags=False,
1199 archive=False,
1200 optimized_fetch=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001201 prune=False,
1202 submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001203 """Perform only the network IO portion of the sync process.
1204 Local working directory/branch state is not affected.
1205 """
Julien Campergue335f5ef2013-10-16 11:02:35 +02001206 if archive and not isinstance(self, MetaProject):
1207 if self.remote.url.startswith(('http://', 'https://')):
David Pursehousef33929d2015-08-24 14:39:14 +09001208 _error("%s: Cannot fetch archives from http/https remotes.", self.name)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001209 return False
1210
1211 name = self.relpath.replace('\\', '/')
1212 name = name.replace('/', '_')
1213 tarpath = '%s.tar' % name
1214 topdir = self.manifest.topdir
1215
1216 try:
1217 self._FetchArchive(tarpath, cwd=topdir)
1218 except GitError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001219 _error('%s', e)
Julien Campergue335f5ef2013-10-16 11:02:35 +02001220 return False
1221
1222 # From now on, we only need absolute tarpath
1223 tarpath = os.path.join(topdir, tarpath)
1224
1225 if not self._ExtractArchive(tarpath, path=topdir):
1226 return False
1227 try:
1228 os.remove(tarpath)
1229 except OSError as e:
David Pursehousef33929d2015-08-24 14:39:14 +09001230 _warn("Cannot remove archive %s: %s", tarpath, str(e))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001231 self._CopyAndLinkFiles()
Julien Campergue335f5ef2013-10-16 11:02:35 +02001232 return True
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001233 if is_new is None:
1234 is_new = not self.Exists
Shawn O. Pearce88443382010-10-08 10:02:09 +02001235 if is_new:
Kevin Degiabaa7f32014-11-12 11:27:45 -07001236 self._InitGitDir(force_sync=force_sync)
Jimmie Westera0444582012-10-24 13:44:42 +02001237 else:
1238 self._UpdateHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001239 self._InitRemote()
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001240
1241 if is_new:
1242 alt = os.path.join(self.gitdir, 'objects/info/alternates')
1243 try:
1244 fd = open(alt, 'rb')
1245 try:
1246 alt_dir = fd.readline().rstrip()
1247 finally:
1248 fd.close()
1249 except IOError:
1250 alt_dir = None
1251 else:
1252 alt_dir = None
1253
Shawn O. Pearcee02ac0a2012-03-14 15:36:59 -07001254 if clone_bundle \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001255 and alt_dir is None \
1256 and self._ApplyCloneBundle(initial=is_new, quiet=quiet):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001257 is_new = False
1258
Shawn O. Pearce6ba6ba02012-05-24 09:46:50 -07001259 if not current_branch_only:
1260 if self.sync_c:
1261 current_branch_only = True
1262 elif not self.manifest._loaded:
1263 # Manifest cannot check defaults until it syncs.
1264 current_branch_only = False
1265 elif self.manifest.default.sync_c:
1266 current_branch_only = True
1267
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001268 if self.clone_depth:
1269 depth = self.clone_depth
1270 else:
1271 depth = self.manifest.manifestProject.config.GetString('repo.depth')
1272
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001273 need_to_fetch = not (optimized_fetch and
1274 (ID_RE.match(self.revisionExpr) and
1275 self._CheckForSha1()))
1276 if (need_to_fetch and
1277 not self._RemoteFetch(initial=is_new, quiet=quiet, alt_dir=alt_dir,
1278 current_branch_only=current_branch_only,
Martin Kellye4e94d22017-03-21 16:05:12 -07001279 no_tags=no_tags, prune=prune, depth=depth,
1280 submodules=submodules)):
Anthony King7bdac712014-07-16 12:56:40 +01001281 return False
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001282
1283 if self.worktree:
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08001284 self._InitMRef()
1285 else:
1286 self._InitMirrorHead()
1287 try:
1288 os.remove(os.path.join(self.gitdir, 'FETCH_HEAD'))
1289 except OSError:
1290 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001291 return True
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08001292
1293 def PostRepoUpgrade(self):
1294 self._InitHooks()
1295
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001296 def _CopyAndLinkFiles(self):
Simran Basib9a1b732015-08-20 12:19:28 -07001297 if self.manifest.isGitcClient:
1298 return
David Pursehouse8a68ff92012-09-24 12:15:13 +09001299 for copyfile in self.copyfiles:
1300 copyfile._Copy()
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001301 for linkfile in self.linkfiles:
1302 linkfile._Link()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001303
Julien Camperguedd654222014-01-09 16:21:37 +01001304 def GetCommitRevisionId(self):
1305 """Get revisionId of a commit.
1306
1307 Use this method instead of GetRevisionId to get the id of the commit rather
1308 than the id of the current git object (for example, a tag)
1309
1310 """
1311 if not self.revisionExpr.startswith(R_TAGS):
1312 return self.GetRevisionId(self._allrefs)
1313
1314 try:
1315 return self.bare_git.rev_list(self.revisionExpr, '-1')[0]
1316 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001317 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1318 (self.revisionExpr, self.name))
Julien Camperguedd654222014-01-09 16:21:37 +01001319
David Pursehouse8a68ff92012-09-24 12:15:13 +09001320 def GetRevisionId(self, all_refs=None):
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001321 if self.revisionId:
1322 return self.revisionId
1323
1324 rem = self.GetRemote(self.remote.name)
1325 rev = rem.ToLocal(self.revisionExpr)
1326
David Pursehouse8a68ff92012-09-24 12:15:13 +09001327 if all_refs is not None and rev in all_refs:
1328 return all_refs[rev]
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001329
1330 try:
1331 return self.bare_git.rev_parse('--verify', '%s^0' % rev)
1332 except GitError:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001333 raise ManifestInvalidRevisionError('revision %s in %s not found' %
1334 (self.revisionExpr, self.name))
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001335
Martin Kellye4e94d22017-03-21 16:05:12 -07001336 def Sync_LocalHalf(self, syncbuf, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001337 """Perform only the local IO portion of the sync process.
1338 Network access is not required.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001339 """
Martin Kellye4e94d22017-03-21 16:05:12 -07001340 self._InitWorkTree(force_sync=force_sync, submodules=submodules)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001341 all_refs = self.bare_ref.all
1342 self.CleanPublishedCache(all_refs)
1343 revid = self.GetRevisionId(all_refs)
Skyler Kaufman835cd682011-03-08 12:14:41 -08001344
David Pursehouse1d947b32012-10-25 12:23:11 +09001345 def _doff():
1346 self._FastForward(revid)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001347 self._CopyAndLinkFiles()
David Pursehouse1d947b32012-10-25 12:23:11 +09001348
Martin Kellye4e94d22017-03-21 16:05:12 -07001349 def _dosubmodules():
1350 self._SyncSubmodules(quiet=True)
1351
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001352 head = self.work_git.GetHead()
1353 if head.startswith(R_HEADS):
1354 branch = head[len(R_HEADS):]
1355 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001356 head = all_refs[head]
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001357 except KeyError:
1358 head = None
1359 else:
1360 branch = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001361
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001362 if branch is None or syncbuf.detach_head:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001363 # Currently on a detached HEAD. The user is assumed to
1364 # not have any local modifications worth worrying about.
1365 #
Shawn O. Pearce3d2cdd02009-04-18 15:26:10 -07001366 if self.IsRebaseInProgress():
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001367 syncbuf.fail(self, _PriorSyncFailedError())
1368 return
1369
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001370 if head == revid:
1371 # No changes; don't do anything further.
Florian Vallee7cf1b362012-06-07 17:11:42 +02001372 # Except if the head needs to be detached
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001373 #
Florian Vallee7cf1b362012-06-07 17:11:42 +02001374 if not syncbuf.detach_head:
Dan Willemsen029eaf32015-09-03 12:52:28 -07001375 # The copy/linkfile config may have changed.
1376 self._CopyAndLinkFiles()
Florian Vallee7cf1b362012-06-07 17:11:42 +02001377 return
1378 else:
1379 lost = self._revlist(not_rev(revid), HEAD)
1380 if lost:
1381 syncbuf.info(self, "discarding %d commits", len(lost))
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001382
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001383 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001384 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001385 if submodules:
1386 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001387 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001388 syncbuf.fail(self, e)
1389 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001390 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001391 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001392
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001393 if head == revid:
1394 # No changes; don't do anything further.
1395 #
Dan Willemsen029eaf32015-09-03 12:52:28 -07001396 # The copy/linkfile config may have changed.
1397 self._CopyAndLinkFiles()
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07001398 return
1399
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001400 branch = self.GetBranch(branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001401
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001402 if not branch.LocalMerge:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001403 # The current branch has no tracking configuration.
Anatol Pomazau2a32f6a2011-08-30 10:52:33 -07001404 # Jump off it to a detached HEAD.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001405 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001406 syncbuf.info(self,
1407 "leaving %s; does not track upstream",
1408 branch.name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001409 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001410 self._Checkout(revid, quiet=True)
Martin Kellye4e94d22017-03-21 16:05:12 -07001411 if submodules:
1412 self._SyncSubmodules(quiet=True)
Sarah Owensa5be53f2012-09-09 15:37:57 -07001413 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001414 syncbuf.fail(self, e)
1415 return
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001416 self._CopyAndLinkFiles()
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001417 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001418
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001419 upstream_gain = self._revlist(not_rev(HEAD), revid)
David Pursehouse8a68ff92012-09-24 12:15:13 +09001420 pub = self.WasPublished(branch.name, all_refs)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001421 if pub:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001422 not_merged = self._revlist(not_rev(revid), pub)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001423 if not_merged:
1424 if upstream_gain:
1425 # The user has published this branch and some of those
1426 # commits are not yet merged upstream. We do not want
1427 # to rewrite the published commits so we punt.
1428 #
Daniel Sandler4c50dee2010-03-02 15:38:03 -05001429 syncbuf.fail(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001430 "branch %s is published (but not merged) and is now "
1431 "%d commits behind" % (branch.name, len(upstream_gain)))
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001432 return
Shawn O. Pearce05f66b62009-04-21 08:26:32 -07001433 elif pub == head:
1434 # All published commits are merged, and thus we are a
1435 # strict subset. We can fast-forward safely.
Shawn O. Pearcea54c5272008-10-30 11:03:00 -07001436 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001437 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001438 if submodules:
1439 syncbuf.later1(self, _dosubmodules)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001440 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001441
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001442 # Examine the local commits not in the remote. Find the
1443 # last one attributed to this user, if any.
1444 #
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001445 local_changes = self._revlist(not_rev(revid), HEAD, format='%H %ce')
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001446 last_mine = None
1447 cnt_mine = 0
1448 for commit in local_changes:
Chirayu Desai0eb35cb2013-11-19 18:46:29 +05301449 commit_id, committer_email = commit.decode('utf-8').split(' ', 1)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001450 if committer_email == self.UserEmail:
1451 last_mine = commit_id
1452 cnt_mine += 1
1453
Shawn O. Pearceda88ff42009-06-03 11:09:12 -07001454 if not upstream_gain and cnt_mine == len(local_changes):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001455 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001456
1457 if self.IsDirty(consider_untracked=False):
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001458 syncbuf.fail(self, _DirtyError())
1459 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001460
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001461 # If the upstream switched on us, warn the user.
1462 #
1463 if branch.merge != self.revisionExpr:
1464 if branch.merge and self.revisionExpr:
1465 syncbuf.info(self,
1466 'manifest switched %s...%s',
1467 branch.merge,
1468 self.revisionExpr)
1469 elif branch.merge:
1470 syncbuf.info(self,
1471 'manifest no longer tracks %s',
1472 branch.merge)
1473
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001474 if cnt_mine < len(local_changes):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001475 # Upstream rebased. Not everything in HEAD
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001476 # was created by this user.
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001477 #
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001478 syncbuf.info(self,
1479 "discarding %d commits removed from upstream",
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001480 len(local_changes) - cnt_mine)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001481
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001482 branch.remote = self.GetRemote(self.remote.name)
Anatol Pomazaucd7c5de2012-03-20 13:45:00 -07001483 if not ID_RE.match(self.revisionExpr):
1484 # in case of manifest sync the revisionExpr might be a SHA1
1485 branch.merge = self.revisionExpr
Conley Owens04f2f0e2014-10-01 17:22:46 -07001486 if not branch.merge.startswith('refs/'):
1487 branch.merge = R_HEADS + branch.merge
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001488 branch.Save()
1489
Mike Pontillod3153822012-02-28 11:53:24 -08001490 if cnt_mine > 0 and self.rebase:
Martin Kellye4e94d22017-03-21 16:05:12 -07001491 def _docopyandlink():
1492 self._CopyAndLinkFiles()
1493
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001494 def _dorebase():
Anthony King7bdac712014-07-16 12:56:40 +01001495 self._Rebase(upstream='%s^1' % last_mine, onto=revid)
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001496 syncbuf.later2(self, _dorebase)
Martin Kellye4e94d22017-03-21 16:05:12 -07001497 if submodules:
1498 syncbuf.later2(self, _dosubmodules)
1499 syncbuf.later2(self, _docopyandlink)
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07001500 elif local_changes:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001501 try:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001502 self._ResetHard(revid)
Martin Kellye4e94d22017-03-21 16:05:12 -07001503 if submodules:
1504 self._SyncSubmodules(quiet=True)
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001505 self._CopyAndLinkFiles()
Sarah Owensa5be53f2012-09-09 15:37:57 -07001506 except GitError as e:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001507 syncbuf.fail(self, e)
1508 return
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001509 else:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07001510 syncbuf.later1(self, _doff)
Martin Kellye4e94d22017-03-21 16:05:12 -07001511 if submodules:
1512 syncbuf.later1(self, _dosubmodules)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001513
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001514 def AddCopyFile(self, src, dest, absdest):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001515 # dest should already be an absolute path, but src is project relative
1516 # make src an absolute path
Shawn O. Pearcec7a4eef2009-03-05 10:32:38 -08001517 abssrc = os.path.join(self.worktree, src)
1518 self.copyfiles.append(_CopyFile(src, dest, abssrc, absdest))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001519
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001520 def AddLinkFile(self, src, dest, absdest):
1521 # dest should already be an absolute path, but src is project relative
Colin Cross0184dcc2015-05-05 00:24:54 -07001522 # make src relative path to dest
1523 absdestdir = os.path.dirname(absdest)
1524 relsrc = os.path.relpath(os.path.join(self.worktree, src), absdestdir)
Wink Saville4c426ef2015-06-03 08:05:17 -07001525 self.linkfiles.append(_LinkFile(self.worktree, src, dest, relsrc, absdest))
Jeff Hamiltone0df2322014-04-21 17:10:59 -05001526
James W. Mills24c13082012-04-12 15:04:13 -05001527 def AddAnnotation(self, name, value, keep):
1528 self.annotations.append(_Annotation(name, value, keep))
1529
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001530 def DownloadPatchSet(self, change_id, patch_id):
1531 """Download a single patch set of a single change to FETCH_HEAD.
1532 """
1533 remote = self.GetRemote(self.remote.name)
1534
1535 cmd = ['fetch', remote.name]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001536 cmd.append('refs/changes/%2.2d/%d/%d'
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001537 % (change_id % 100, change_id, patch_id))
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001538 if GitCommand(self, cmd, bare=True).Wait() != 0:
1539 return None
1540 return DownloadedChange(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001541 self.GetRevisionId(),
Shawn O. Pearce632768b2008-10-23 11:58:52 -07001542 change_id,
1543 patch_id,
1544 self.bare_git.rev_parse('FETCH_HEAD'))
1545
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001546
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001547# Branch Management ##
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001548
Simran Basib9a1b732015-08-20 12:19:28 -07001549 def StartBranch(self, name, branch_merge=''):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001550 """Create a new branch off the manifest's revision.
1551 """
Simran Basib9a1b732015-08-20 12:19:28 -07001552 if not branch_merge:
1553 branch_merge = self.revisionExpr
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001554 head = self.work_git.GetHead()
1555 if head == (R_HEADS + name):
1556 return True
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001557
David Pursehouse8a68ff92012-09-24 12:15:13 +09001558 all_refs = self.bare_ref.all
Anthony King7bdac712014-07-16 12:56:40 +01001559 if R_HEADS + name in all_refs:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001560 return GitCommand(self,
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001561 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001562 capture_stdout=True,
1563 capture_stderr=True).Wait() == 0
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001564
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001565 branch = self.GetBranch(name)
1566 branch.remote = self.GetRemote(self.remote.name)
Simran Basib9a1b732015-08-20 12:19:28 -07001567 branch.merge = branch_merge
1568 if not branch.merge.startswith('refs/') and not ID_RE.match(branch_merge):
1569 branch.merge = R_HEADS + branch_merge
David Pursehouse8a68ff92012-09-24 12:15:13 +09001570 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce0a389e92009-04-10 16:21:18 -07001571
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001572 if head.startswith(R_HEADS):
1573 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001574 head = all_refs[head]
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001575 except KeyError:
1576 head = None
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001577 if revid and head and revid == head:
1578 ref = os.path.join(self.gitdir, R_HEADS + name)
1579 try:
1580 os.makedirs(os.path.dirname(ref))
1581 except OSError:
1582 pass
1583 _lwrite(ref, '%s\n' % revid)
1584 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1585 'ref: %s%s\n' % (R_HEADS, name))
1586 branch.Save()
1587 return True
1588
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001589 if GitCommand(self,
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001590 ['checkout', '-b', branch.name, revid],
Anthony King7bdac712014-07-16 12:56:40 +01001591 capture_stdout=True,
1592 capture_stderr=True).Wait() == 0:
Shawn O. Pearceaccc56d2009-04-18 14:45:51 -07001593 branch.Save()
1594 return True
1595 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001596
Wink Saville02d79452009-04-10 13:01:24 -07001597 def CheckoutBranch(self, name):
1598 """Checkout a local topic branch.
Doug Anderson3ba5f952011-04-07 12:51:04 -07001599
1600 Args:
1601 name: The name of the branch to checkout.
1602
1603 Returns:
1604 True if the checkout succeeded; False if it didn't; None if the branch
1605 didn't exist.
Wink Saville02d79452009-04-10 13:01:24 -07001606 """
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001607 rev = R_HEADS + name
1608 head = self.work_git.GetHead()
1609 if head == rev:
1610 # Already on the branch
1611 #
1612 return True
Wink Saville02d79452009-04-10 13:01:24 -07001613
David Pursehouse8a68ff92012-09-24 12:15:13 +09001614 all_refs = self.bare_ref.all
Wink Saville02d79452009-04-10 13:01:24 -07001615 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001616 revid = all_refs[rev]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001617 except KeyError:
1618 # Branch does not exist in this project
1619 #
Doug Anderson3ba5f952011-04-07 12:51:04 -07001620 return None
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001621
1622 if head.startswith(R_HEADS):
1623 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09001624 head = all_refs[head]
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001625 except KeyError:
1626 head = None
1627
1628 if head == revid:
1629 # Same revision; just update HEAD to point to the new
1630 # target branch, but otherwise take no other action.
1631 #
1632 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1633 'ref: %s%s\n' % (R_HEADS, name))
1634 return True
Wink Saville02d79452009-04-10 13:01:24 -07001635
Shawn O. Pearce89e717d2009-04-18 15:04:41 -07001636 return GitCommand(self,
1637 ['checkout', name, '--'],
Anthony King7bdac712014-07-16 12:56:40 +01001638 capture_stdout=True,
1639 capture_stderr=True).Wait() == 0
Wink Saville02d79452009-04-10 13:01:24 -07001640
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001641 def AbandonBranch(self, name):
1642 """Destroy a local topic branch.
Doug Andersondafb1d62011-04-07 11:46:59 -07001643
1644 Args:
1645 name: The name of the branch to abandon.
1646
1647 Returns:
1648 True if the abandon succeeded; False if it didn't; None if the branch
1649 didn't exist.
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001650 """
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001651 rev = R_HEADS + name
David Pursehouse8a68ff92012-09-24 12:15:13 +09001652 all_refs = self.bare_ref.all
1653 if rev not in all_refs:
Doug Andersondafb1d62011-04-07 11:46:59 -07001654 # Doesn't exist
1655 return None
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001656
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001657 head = self.work_git.GetHead()
1658 if head == rev:
1659 # We can't destroy the branch while we are sitting
1660 # on it. Switch to a detached HEAD.
1661 #
David Pursehouse8a68ff92012-09-24 12:15:13 +09001662 head = all_refs[head]
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001663
David Pursehouse8a68ff92012-09-24 12:15:13 +09001664 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001665 if head == revid:
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001666 _lwrite(os.path.join(self.worktree, '.git', HEAD),
1667 '%s\n' % revid)
1668 else:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001669 self._Checkout(revid, quiet=True)
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001670
Shawn O. Pearce552ac892009-04-18 15:15:24 -07001671 return GitCommand(self,
1672 ['branch', '-D', name],
Anthony King7bdac712014-07-16 12:56:40 +01001673 capture_stdout=True,
1674 capture_stderr=True).Wait() == 0
Shawn O. Pearce9fa44db2008-11-03 11:24:59 -08001675
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001676 def PruneHeads(self):
1677 """Prune any topic branches already merged into upstream.
1678 """
1679 cb = self.CurrentBranch
1680 kill = []
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001681 left = self._allrefs
1682 for name in left.keys():
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001683 if name.startswith(R_HEADS):
1684 name = name[len(R_HEADS):]
1685 if cb is None or name != cb:
1686 kill.append(name)
1687
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07001688 rev = self.GetRevisionId(left)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001689 if cb is not None \
1690 and not self._revlist(HEAD + '...' + rev) \
Anthony King7bdac712014-07-16 12:56:40 +01001691 and not self.IsDirty(consider_untracked=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001692 self.work_git.DetachHead(HEAD)
1693 kill.append(cb)
1694
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001695 if kill:
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07001696 old = self.bare_git.GetHead()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001697
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001698 try:
1699 self.bare_git.DetachHead(rev)
1700
1701 b = ['branch', '-d']
1702 b.extend(kill)
1703 b = GitCommand(self, b, bare=True,
1704 capture_stdout=True,
1705 capture_stderr=True)
1706 b.Wait()
1707 finally:
Dan Willemsen1a799d12015-12-15 13:40:05 -08001708 if ID_RE.match(old):
1709 self.bare_git.DetachHead(old)
1710 else:
1711 self.bare_git.SetHead(old)
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001712 left = self._allrefs
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001713
Shawn O. Pearce3778f9d2009-03-02 12:30:50 -08001714 for branch in kill:
1715 if (R_HEADS + branch) not in left:
1716 self.CleanPublishedCache()
1717 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001718
1719 if cb and cb not in kill:
1720 kill.append(cb)
Shawn O. Pearce7c6c64d2009-03-02 12:38:13 -08001721 kill.sort()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001722
1723 kept = []
1724 for branch in kill:
Anthony King7bdac712014-07-16 12:56:40 +01001725 if R_HEADS + branch in left:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001726 branch = self.GetBranch(branch)
1727 base = branch.LocalMerge
1728 if not base:
1729 base = rev
1730 kept.append(ReviewableBranch(self, branch, base))
1731 return kept
1732
1733
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001734# Submodule Management ##
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001735
1736 def GetRegisteredSubprojects(self):
1737 result = []
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001738
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001739 def rec(subprojects):
1740 if not subprojects:
1741 return
1742 result.extend(subprojects)
1743 for p in subprojects:
1744 rec(p.subprojects)
1745 rec(self.subprojects)
1746 return result
1747
1748 def _GetSubmodules(self):
1749 # Unfortunately we cannot call `git submodule status --recursive` here
1750 # because the working tree might not exist yet, and it cannot be used
1751 # without a working tree in its current implementation.
1752
1753 def get_submodules(gitdir, rev):
1754 # Parse .gitmodules for submodule sub_paths and sub_urls
1755 sub_paths, sub_urls = parse_gitmodules(gitdir, rev)
1756 if not sub_paths:
1757 return []
1758 # Run `git ls-tree` to read SHAs of submodule object, which happen to be
1759 # revision of submodule repository
1760 sub_revs = git_ls_tree(gitdir, rev, sub_paths)
1761 submodules = []
1762 for sub_path, sub_url in zip(sub_paths, sub_urls):
1763 try:
1764 sub_rev = sub_revs[sub_path]
1765 except KeyError:
1766 # Ignore non-exist submodules
1767 continue
1768 submodules.append((sub_rev, sub_path, sub_url))
1769 return submodules
1770
1771 re_path = re.compile(r'^submodule\.([^.]+)\.path=(.*)$')
1772 re_url = re.compile(r'^submodule\.([^.]+)\.url=(.*)$')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001773
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001774 def parse_gitmodules(gitdir, rev):
1775 cmd = ['cat-file', 'blob', '%s:.gitmodules' % rev]
1776 try:
Anthony King7bdac712014-07-16 12:56:40 +01001777 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1778 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001779 except GitError:
1780 return [], []
1781 if p.Wait() != 0:
1782 return [], []
1783
1784 gitmodules_lines = []
1785 fd, temp_gitmodules_path = tempfile.mkstemp()
1786 try:
1787 os.write(fd, p.stdout)
1788 os.close(fd)
1789 cmd = ['config', '--file', temp_gitmodules_path, '--list']
Anthony King7bdac712014-07-16 12:56:40 +01001790 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1791 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001792 if p.Wait() != 0:
1793 return [], []
1794 gitmodules_lines = p.stdout.split('\n')
1795 except GitError:
1796 return [], []
1797 finally:
1798 os.remove(temp_gitmodules_path)
1799
1800 names = set()
1801 paths = {}
1802 urls = {}
1803 for line in gitmodules_lines:
1804 if not line:
1805 continue
1806 m = re_path.match(line)
1807 if m:
1808 names.add(m.group(1))
1809 paths[m.group(1)] = m.group(2)
1810 continue
1811 m = re_url.match(line)
1812 if m:
1813 names.add(m.group(1))
1814 urls[m.group(1)] = m.group(2)
1815 continue
1816 names = sorted(names)
1817 return ([paths.get(name, '') for name in names],
1818 [urls.get(name, '') for name in names])
1819
1820 def git_ls_tree(gitdir, rev, paths):
1821 cmd = ['ls-tree', rev, '--']
1822 cmd.extend(paths)
1823 try:
Anthony King7bdac712014-07-16 12:56:40 +01001824 p = GitCommand(None, cmd, capture_stdout=True, capture_stderr=True,
1825 bare=True, gitdir=gitdir)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001826 except GitError:
1827 return []
1828 if p.Wait() != 0:
1829 return []
1830 objects = {}
1831 for line in p.stdout.split('\n'):
1832 if not line.strip():
1833 continue
1834 object_rev, object_path = line.split()[2:4]
1835 objects[object_path] = object_rev
1836 return objects
1837
1838 try:
1839 rev = self.GetRevisionId()
1840 except GitError:
1841 return []
1842 return get_submodules(self.gitdir, rev)
1843
1844 def GetDerivedSubprojects(self):
1845 result = []
1846 if not self.Exists:
1847 # If git repo does not exist yet, querying its submodules will
1848 # mess up its states; so return here.
1849 return result
1850 for rev, path, url in self._GetSubmodules():
1851 name = self.manifest.GetSubprojectName(self, path)
David James8d201162013-10-11 17:03:19 -07001852 relpath, worktree, gitdir, objdir = \
1853 self.manifest.GetSubprojectPaths(self, name, path)
1854 project = self.manifest.paths.get(relpath)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001855 if project:
1856 result.extend(project.GetDerivedSubprojects())
1857 continue
David James8d201162013-10-11 17:03:19 -07001858
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001859 remote = RemoteSpec(self.remote.name,
Anthony King7bdac712014-07-16 12:56:40 +01001860 url=url,
Steve Raed6480452016-08-10 15:00:00 -07001861 pushUrl=self.remote.pushUrl,
Anthony King7bdac712014-07-16 12:56:40 +01001862 review=self.remote.review,
1863 revision=self.remote.revision)
1864 subproject = Project(manifest=self.manifest,
1865 name=name,
1866 remote=remote,
1867 gitdir=gitdir,
1868 objdir=objdir,
1869 worktree=worktree,
1870 relpath=relpath,
Aymen Bouaziz2598ed02016-06-24 14:34:08 +02001871 revisionExpr=rev,
Anthony King7bdac712014-07-16 12:56:40 +01001872 revisionId=rev,
1873 rebase=self.rebase,
1874 groups=self.groups,
1875 sync_c=self.sync_c,
1876 sync_s=self.sync_s,
1877 parent=self,
1878 is_derived=True)
Che-Liang Chioub2bd91c2012-01-11 11:28:42 +08001879 result.append(subproject)
1880 result.extend(subproject.GetDerivedSubprojects())
1881 return result
1882
1883
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07001884# Direct Git Commands ##
Chris AtLee2fb64662014-01-16 21:32:33 -05001885 def _CheckForSha1(self):
1886 try:
1887 # if revision (sha or tag) is not present then following function
1888 # throws an error.
1889 self.bare_git.rev_parse('--verify', '%s^0' % self.revisionExpr)
1890 return True
1891 except GitError:
1892 # There is no such persistent revision. We have to fetch it.
1893 return False
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001894
Julien Campergue335f5ef2013-10-16 11:02:35 +02001895 def _FetchArchive(self, tarpath, cwd=None):
1896 cmd = ['archive', '-v', '-o', tarpath]
1897 cmd.append('--remote=%s' % self.remote.url)
1898 cmd.append('--prefix=%s/' % self.relpath)
1899 cmd.append(self.revisionExpr)
1900
1901 command = GitCommand(self, cmd, cwd=cwd,
1902 capture_stdout=True,
1903 capture_stderr=True)
1904
1905 if command.Wait() != 0:
1906 raise GitError('git archive %s: %s' % (self.name, command.stderr))
1907
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001908 def _RemoteFetch(self, name=None,
1909 current_branch_only=False,
Shawn O. Pearce16614f82010-10-29 12:05:43 -07001910 initial=False,
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001911 quiet=False,
Mitchel Humpherys597868b2012-10-29 10:18:34 -07001912 alt_dir=None,
David Pursehouse74cfd272015-10-14 10:50:15 +09001913 no_tags=False,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001914 prune=False,
Martin Kellye4e94d22017-03-21 16:05:12 -07001915 depth=None,
1916 submodules=False):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001917
1918 is_sha1 = False
1919 tag_name = None
David Pursehouse9bc422f2014-04-15 10:28:56 +09001920 # The depth should not be used when fetching to a mirror because
1921 # it will result in a shallow repository that cannot be cloned or
1922 # fetched from.
Aymen Bouaziz6c594462016-10-25 18:03:51 +02001923 # The repo project should also never be synced with partial depth.
1924 if self.manifest.IsMirror or self.relpath == '.repo/repo':
1925 depth = None
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001926
Shawn Pearce69e04d82014-01-29 12:48:54 -08001927 if depth:
1928 current_branch_only = True
1929
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001930 if ID_RE.match(self.revisionExpr) is not None:
1931 is_sha1 = True
1932
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001933 if current_branch_only:
Nasser Grainawi909d58b2014-09-19 12:13:04 -06001934 if self.revisionExpr.startswith(R_TAGS):
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001935 # this is a tag and its sha1 value should never change
1936 tag_name = self.revisionExpr[len(R_TAGS):]
1937
1938 if is_sha1 or tag_name is not None:
Chris AtLee2fb64662014-01-16 21:32:33 -05001939 if self._CheckForSha1():
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001940 return True
Bertrand SIMONNET3000cda2014-11-25 16:19:29 -08001941 if is_sha1 and not depth:
1942 # When syncing a specific commit and --depth is not set:
1943 # * if upstream is explicitly specified and is not a sha1, fetch only
1944 # upstream as users expect only upstream to be fetch.
1945 # Note: The commit might not be in upstream in which case the sync
1946 # will fail.
1947 # * otherwise, fetch all branches to make sure we end up with the
1948 # specific commit.
Aymen Bouaziz037040f2016-06-28 12:27:23 +02001949 if self.upstream:
1950 current_branch_only = not ID_RE.match(self.upstream)
1951 else:
1952 current_branch_only = False
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07001953
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07001954 if not name:
1955 name = self.remote.name
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001956
1957 ssh_proxy = False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07001958 remote = self.GetRemote(name)
1959 if remote.PreConnectFetch():
Shawn O. Pearcefb231612009-04-10 18:53:46 -07001960 ssh_proxy = True
1961
Shawn O. Pearce88443382010-10-08 10:02:09 +02001962 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07001963 if alt_dir and 'objects' == os.path.basename(alt_dir):
1964 ref_dir = os.path.dirname(alt_dir)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001965 packed_refs = os.path.join(self.gitdir, 'packed-refs')
1966 remote = self.GetRemote(name)
1967
David Pursehouse8a68ff92012-09-24 12:15:13 +09001968 all_refs = self.bare_ref.all
1969 ids = set(all_refs.values())
Shawn O. Pearce88443382010-10-08 10:02:09 +02001970 tmp = set()
1971
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301972 for r, ref_id in GitRefs(ref_dir).all.items():
David Pursehouse8a68ff92012-09-24 12:15:13 +09001973 if r not in all_refs:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001974 if r.startswith(R_TAGS) or remote.WritesTo(r):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001975 all_refs[r] = ref_id
1976 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001977 continue
1978
David Pursehouse8a68ff92012-09-24 12:15:13 +09001979 if ref_id in ids:
Shawn O. Pearce88443382010-10-08 10:02:09 +02001980 continue
1981
David Pursehouse8a68ff92012-09-24 12:15:13 +09001982 r = 'refs/_alt/%s' % ref_id
1983 all_refs[r] = ref_id
1984 ids.add(ref_id)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001985 tmp.add(r)
1986
heping3d7bbc92017-04-12 19:51:47 +08001987 tmp_packed_lines = []
1988 old_packed_lines = []
Shawn O. Pearce88443382010-10-08 10:02:09 +02001989
Chirayu Desai217ea7d2013-03-01 19:14:38 +05301990 for r in sorted(all_refs):
David Pursehouse8a68ff92012-09-24 12:15:13 +09001991 line = '%s %s\n' % (all_refs[r], r)
heping3d7bbc92017-04-12 19:51:47 +08001992 tmp_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001993 if r not in tmp:
heping3d7bbc92017-04-12 19:51:47 +08001994 old_packed_lines.append(line)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001995
heping3d7bbc92017-04-12 19:51:47 +08001996 tmp_packed = ''.join(tmp_packed_lines)
1997 old_packed = ''.join(old_packed_lines)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001998 _lwrite(packed_refs, tmp_packed)
Shawn O. Pearce88443382010-10-08 10:02:09 +02001999 else:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002000 alt_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002001
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002002 cmd = ['fetch']
Doug Anderson30d45292011-05-04 15:01:04 -07002003
Conley Owensf97e8382015-01-21 11:12:46 -08002004 if depth:
Doug Anderson30d45292011-05-04 15:01:04 -07002005 cmd.append('--depth=%s' % depth)
Dan Willemseneeab6862015-08-03 13:11:53 -07002006 else:
2007 # If this repo has shallow objects, then we don't know which refs have
2008 # shallow objects or not. Tell git to unshallow all fetched refs. Don't
2009 # do this with projects that don't have shallow objects, since it is less
2010 # efficient.
2011 if os.path.exists(os.path.join(self.gitdir, 'shallow')):
2012 cmd.append('--depth=2147483647')
Doug Anderson30d45292011-05-04 15:01:04 -07002013
Shawn O. Pearce16614f82010-10-29 12:05:43 -07002014 if quiet:
2015 cmd.append('--quiet')
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002016 if not self.worktree:
2017 cmd.append('--update-head-ok')
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002018 cmd.append(name)
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002019
Mitchel Humpherys26c45a72014-03-10 14:21:59 -07002020 # If using depth then we should not get all the tags since they may
2021 # be outside of the depth.
2022 if no_tags or depth:
2023 cmd.append('--no-tags')
2024 else:
2025 cmd.append('--tags')
2026
David Pursehouse74cfd272015-10-14 10:50:15 +09002027 if prune:
2028 cmd.append('--prune')
2029
Martin Kellye4e94d22017-03-21 16:05:12 -07002030 if submodules:
2031 cmd.append('--recurse-submodules=on-demand')
2032
Conley Owens80b87fe2014-05-09 17:13:44 -07002033 spec = []
Brian Harring14a66742012-09-28 20:21:57 -07002034 if not current_branch_only:
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002035 # Fetch whole repo
Conley Owens80b87fe2014-05-09 17:13:44 -07002036 spec.append(str((u'+refs/heads/*:') + remote.ToLocal('refs/heads/*')))
Anatol Pomazau53d6f4d2011-08-25 17:21:47 -07002037 elif tag_name is not None:
Conley Owens80b87fe2014-05-09 17:13:44 -07002038 spec.append('tag')
2039 spec.append(tag_name)
Nasser Grainawi04e52d62014-09-30 13:34:52 -06002040
David Pursehouse403b64e2015-04-27 10:41:33 +09002041 if not self.manifest.IsMirror:
2042 branch = self.revisionExpr
Kevin Degi679bac42015-06-22 15:31:26 -06002043 if is_sha1 and depth and git_require((1, 8, 3)):
David Pursehouse403b64e2015-04-27 10:41:33 +09002044 # Shallow checkout of a specific commit, fetch from that commit and not
2045 # the heads only as the commit might be deeper in the history.
2046 spec.append(branch)
2047 else:
2048 if is_sha1:
2049 branch = self.upstream
2050 if branch is not None and branch.strip():
2051 if not branch.startswith('refs/'):
2052 branch = R_HEADS + branch
2053 spec.append(str((u'+%s:' % branch) + remote.ToLocal(branch)))
Conley Owens80b87fe2014-05-09 17:13:44 -07002054 cmd.extend(spec)
2055
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002056 ok = False
David Pursehouse8a68ff92012-09-24 12:15:13 +09002057 for _i in range(2):
John L. Villalovos9c76f672015-03-16 20:49:10 -07002058 gitcmd = GitCommand(self, cmd, bare=True, ssh_proxy=ssh_proxy)
John L. Villalovos126e2982015-01-29 21:58:12 -08002059 ret = gitcmd.Wait()
Brian Harring14a66742012-09-28 20:21:57 -07002060 if ret == 0:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002061 ok = True
2062 break
John L. Villalovos126e2982015-01-29 21:58:12 -08002063 # If needed, run the 'git remote prune' the first time through the loop
2064 elif (not _i and
2065 "error:" in gitcmd.stderr and
2066 "git remote prune" in gitcmd.stderr):
2067 prunecmd = GitCommand(self, ['remote', 'prune', name], bare=True,
John L. Villalovos9c76f672015-03-16 20:49:10 -07002068 ssh_proxy=ssh_proxy)
John L. Villalovose30f46b2015-02-25 14:27:02 -08002069 ret = prunecmd.Wait()
John L. Villalovose30f46b2015-02-25 14:27:02 -08002070 if ret:
John L. Villalovos126e2982015-01-29 21:58:12 -08002071 break
2072 continue
Brian Harring14a66742012-09-28 20:21:57 -07002073 elif current_branch_only and is_sha1 and ret == 128:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002074 # Exit code 128 means "couldn't find the ref you asked for"; if we're
2075 # in sha1 mode, we just tried sync'ing from the upstream field; it
2076 # doesn't exist, thus abort the optimization attempt and do a full sync.
Brian Harring14a66742012-09-28 20:21:57 -07002077 break
Colin Crossc4b301f2015-05-13 00:10:02 -07002078 elif ret < 0:
2079 # Git died with a signal, exit immediately
2080 break
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002081 time.sleep(random.randint(30, 45))
Shawn O. Pearce88443382010-10-08 10:02:09 +02002082
2083 if initial:
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002084 if alt_dir:
Shawn O. Pearce88443382010-10-08 10:02:09 +02002085 if old_packed != '':
2086 _lwrite(packed_refs, old_packed)
2087 else:
2088 os.remove(packed_refs)
2089 self.bare_git.pack_refs('--all', '--prune')
Brian Harring14a66742012-09-28 20:21:57 -07002090
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002091 if is_sha1 and current_branch_only:
Brian Harring14a66742012-09-28 20:21:57 -07002092 # We just synced the upstream given branch; verify we
2093 # got what we wanted, else trigger a second run of all
2094 # refs.
Chris AtLee2fb64662014-01-16 21:32:33 -05002095 if not self._CheckForSha1():
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002096 if current_branch_only and depth:
2097 # Sync the current branch only with depth set to None
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002098 return self._RemoteFetch(name=name,
2099 current_branch_only=current_branch_only,
Aymen Bouaziz6c594462016-10-25 18:03:51 +02002100 initial=False, quiet=quiet, alt_dir=alt_dir,
2101 depth=None)
2102 else:
2103 # Avoid infinite recursion: sync all branches with depth set to None
2104 return self._RemoteFetch(name=name, current_branch_only=False,
2105 initial=False, quiet=quiet, alt_dir=alt_dir,
2106 depth=None)
Brian Harring14a66742012-09-28 20:21:57 -07002107
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002108 return ok
2109
2110 def _ApplyCloneBundle(self, initial=False, quiet=False):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002111 if initial and \
2112 (self.manifest.manifestProject.config.GetString('repo.depth') or
2113 self.clone_depth):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002114 return False
2115
2116 remote = self.GetRemote(self.remote.name)
2117 bundle_url = remote.url + '/clone.bundle'
2118 bundle_url = GitConfig.ForUser().UrlInsteadOf(bundle_url)
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002119 if GetSchemeFromUrl(bundle_url) not in ('http', 'https',
2120 'persistent-http',
2121 'persistent-https'):
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002122 return False
2123
2124 bundle_dst = os.path.join(self.gitdir, 'clone.bundle')
2125 bundle_tmp = os.path.join(self.gitdir, 'clone.bundle.tmp')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002126
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002127 exist_dst = os.path.exists(bundle_dst)
2128 exist_tmp = os.path.exists(bundle_tmp)
2129
2130 if not initial and not exist_dst and not exist_tmp:
2131 return False
2132
2133 if not exist_dst:
2134 exist_dst = self._FetchBundle(bundle_url, bundle_tmp, bundle_dst, quiet)
2135 if not exist_dst:
2136 return False
2137
2138 cmd = ['fetch']
2139 if quiet:
2140 cmd.append('--quiet')
2141 if not self.worktree:
2142 cmd.append('--update-head-ok')
2143 cmd.append(bundle_dst)
2144 for f in remote.fetch:
2145 cmd.append(str(f))
2146 cmd.append('refs/tags/*:refs/tags/*')
2147
2148 ok = GitCommand(self, cmd, bare=True).Wait() == 0
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002149 if os.path.exists(bundle_dst):
2150 os.remove(bundle_dst)
2151 if os.path.exists(bundle_tmp):
2152 os.remove(bundle_tmp)
Shawn O. Pearce88443382010-10-08 10:02:09 +02002153 return ok
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002154
Shawn O. Pearcec325dc32011-10-03 08:30:24 -07002155 def _FetchBundle(self, srcUrl, tmpPath, dstPath, quiet):
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002156 if os.path.exists(dstPath):
2157 os.remove(dstPath)
Shawn O. Pearce29472462011-10-11 09:24:07 -07002158
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002159 cmd = ['curl', '--fail', '--output', tmpPath, '--netrc', '--location']
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002160 if quiet:
2161 cmd += ['--silent']
2162 if os.path.exists(tmpPath):
2163 size = os.stat(tmpPath).st_size
2164 if size >= 1024:
2165 cmd += ['--continue-at', '%d' % (size,)]
2166 else:
2167 os.remove(tmpPath)
2168 if 'http_proxy' in os.environ and 'darwin' == sys.platform:
2169 cmd += ['--proxy', os.environ['http_proxy']]
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002170 with GetUrlCookieFile(srcUrl, quiet) as (cookiefile, _proxy):
Dave Borowitz137d0132015-01-02 11:12:54 -08002171 if cookiefile:
Dave Borowitz4abf8e62015-01-02 11:39:04 -08002172 cmd += ['--cookie', cookiefile, '--cookie-jar', cookiefile]
Dave Borowitz137d0132015-01-02 11:12:54 -08002173 if srcUrl.startswith('persistent-'):
2174 srcUrl = srcUrl[len('persistent-'):]
2175 cmd += [srcUrl]
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002176
Dave Borowitz137d0132015-01-02 11:12:54 -08002177 if IsTrace():
2178 Trace('%s', ' '.join(cmd))
2179 try:
2180 proc = subprocess.Popen(cmd)
2181 except OSError:
2182 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002183
Dave Borowitz137d0132015-01-02 11:12:54 -08002184 curlret = proc.wait()
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002185
Dave Borowitz137d0132015-01-02 11:12:54 -08002186 if curlret == 22:
2187 # From curl man page:
2188 # 22: HTTP page not retrieved. The requested url was not found or
2189 # returned another error with the HTTP error code being 400 or above.
2190 # This return code only appears if -f, --fail is used.
2191 if not quiet:
2192 print("Server does not provide clone.bundle; ignoring.",
2193 file=sys.stderr)
2194 return False
Matt Gumbel2dc810c2012-08-30 09:39:36 -07002195
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002196 if os.path.exists(tmpPath):
Kris Giesingc8d882a2014-12-23 13:02:32 -08002197 if curlret == 0 and self._IsValidBundle(tmpPath, quiet):
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002198 os.rename(tmpPath, dstPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002199 return True
2200 else:
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002201 os.remove(tmpPath)
Shawn O. Pearce5e7127d2012-08-02 14:57:37 -07002202 return False
2203 else:
2204 return False
Shawn O. Pearcef322b9a2011-09-19 14:50:58 -07002205
Kris Giesingc8d882a2014-12-23 13:02:32 -08002206 def _IsValidBundle(self, path, quiet):
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002207 try:
2208 with open(path) as f:
2209 if f.read(16) == '# v2 git bundle\n':
2210 return True
2211 else:
Kris Giesingc8d882a2014-12-23 13:02:32 -08002212 if not quiet:
2213 print("Invalid clone.bundle file; ignoring.", file=sys.stderr)
Dave Borowitz91f3ba52013-06-03 12:15:23 -07002214 return False
2215 except OSError:
2216 return False
2217
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002218 def _Checkout(self, rev, quiet=False):
2219 cmd = ['checkout']
2220 if quiet:
2221 cmd.append('-q')
2222 cmd.append(rev)
2223 cmd.append('--')
2224 if GitCommand(self, cmd).Wait() != 0:
2225 if self._allrefs:
2226 raise GitError('%s checkout %s ' % (self.name, rev))
2227
Anthony King7bdac712014-07-16 12:56:40 +01002228 def _CherryPick(self, rev):
Pierre Tardye5a21222011-03-24 16:28:18 +01002229 cmd = ['cherry-pick']
2230 cmd.append(rev)
2231 cmd.append('--')
2232 if GitCommand(self, cmd).Wait() != 0:
2233 if self._allrefs:
2234 raise GitError('%s cherry-pick %s ' % (self.name, rev))
2235
Anthony King7bdac712014-07-16 12:56:40 +01002236 def _Revert(self, rev):
Erwan Mahea94f1622011-08-19 13:56:09 +02002237 cmd = ['revert']
2238 cmd.append('--no-edit')
2239 cmd.append(rev)
2240 cmd.append('--')
2241 if GitCommand(self, cmd).Wait() != 0:
2242 if self._allrefs:
2243 raise GitError('%s revert %s ' % (self.name, rev))
2244
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002245 def _ResetHard(self, rev, quiet=True):
2246 cmd = ['reset', '--hard']
2247 if quiet:
2248 cmd.append('-q')
2249 cmd.append(rev)
2250 if GitCommand(self, cmd).Wait() != 0:
2251 raise GitError('%s reset --hard %s ' % (self.name, rev))
2252
Martin Kellye4e94d22017-03-21 16:05:12 -07002253 def _SyncSubmodules(self, quiet=True):
2254 cmd = ['submodule', 'update', '--init', '--recursive']
2255 if quiet:
2256 cmd.append('-q')
2257 if GitCommand(self, cmd).Wait() != 0:
2258 raise GitError('%s submodule update --init --recursive %s ' % self.name)
2259
Anthony King7bdac712014-07-16 12:56:40 +01002260 def _Rebase(self, upstream, onto=None):
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002261 cmd = ['rebase']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002262 if onto is not None:
2263 cmd.extend(['--onto', onto])
2264 cmd.append(upstream)
Shawn O. Pearce19a83d82009-04-16 08:14:26 -07002265 if GitCommand(self, cmd).Wait() != 0:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002266 raise GitError('%s rebase %s ' % (self.name, upstream))
2267
Pierre Tardy3d125942012-05-04 12:18:12 +02002268 def _FastForward(self, head, ffonly=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002269 cmd = ['merge', head]
Pierre Tardy3d125942012-05-04 12:18:12 +02002270 if ffonly:
2271 cmd.append("--ff-only")
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002272 if GitCommand(self, cmd).Wait() != 0:
2273 raise GitError('%s merge %s ' % (self.name, head))
2274
Kevin Degiabaa7f32014-11-12 11:27:45 -07002275 def _InitGitDir(self, mirror_git=None, force_sync=False):
Kevin Degi384b3c52014-10-16 16:02:58 -06002276 init_git_dir = not os.path.exists(self.gitdir)
2277 init_obj_dir = not os.path.exists(self.objdir)
Kevin Degib1a07b82015-07-27 13:33:43 -06002278 try:
2279 # Initialize the bare repository, which contains all of the objects.
2280 if init_obj_dir:
2281 os.makedirs(self.objdir)
2282 self.bare_objdir.init()
David James8d201162013-10-11 17:03:19 -07002283
Kevin Degib1a07b82015-07-27 13:33:43 -06002284 # If we have a separate directory to hold refs, initialize it as well.
2285 if self.objdir != self.gitdir:
2286 if init_git_dir:
2287 os.makedirs(self.gitdir)
Kevin Degi384b3c52014-10-16 16:02:58 -06002288
Kevin Degib1a07b82015-07-27 13:33:43 -06002289 if init_obj_dir or init_git_dir:
2290 self._ReferenceGitDir(self.objdir, self.gitdir, share_refs=False,
2291 copy_all=True)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002292 try:
2293 self._CheckDirReference(self.objdir, self.gitdir, share_refs=False)
2294 except GitError as e:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002295 if force_sync:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002296 print("Retrying clone after deleting %s" %
2297 self.gitdir, file=sys.stderr)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002298 try:
2299 shutil.rmtree(os.path.realpath(self.gitdir))
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002300 if self.worktree and os.path.exists(os.path.realpath
2301 (self.worktree)):
Kevin Degiabaa7f32014-11-12 11:27:45 -07002302 shutil.rmtree(os.path.realpath(self.worktree))
2303 return self._InitGitDir(mirror_git=mirror_git, force_sync=False)
2304 except:
2305 raise e
2306 raise e
Shawn O. Pearce2816d4f2009-03-03 17:53:18 -08002307
Kevin Degib1a07b82015-07-27 13:33:43 -06002308 if init_git_dir:
2309 mp = self.manifest.manifestProject
2310 ref_dir = mp.config.GetString('repo.reference') or ''
Shawn O. Pearce88443382010-10-08 10:02:09 +02002311
Kevin Degib1a07b82015-07-27 13:33:43 -06002312 if ref_dir or mirror_git:
2313 if not mirror_git:
2314 mirror_git = os.path.join(ref_dir, self.name + '.git')
2315 repo_git = os.path.join(ref_dir, '.repo', 'projects',
2316 self.relpath + '.git')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002317
Kevin Degib1a07b82015-07-27 13:33:43 -06002318 if os.path.exists(mirror_git):
2319 ref_dir = mirror_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002320
Kevin Degib1a07b82015-07-27 13:33:43 -06002321 elif os.path.exists(repo_git):
2322 ref_dir = repo_git
Shawn O. Pearce88443382010-10-08 10:02:09 +02002323
Kevin Degib1a07b82015-07-27 13:33:43 -06002324 else:
2325 ref_dir = None
Shawn O. Pearce88443382010-10-08 10:02:09 +02002326
Kevin Degib1a07b82015-07-27 13:33:43 -06002327 if ref_dir:
2328 _lwrite(os.path.join(self.gitdir, 'objects/info/alternates'),
2329 os.path.join(ref_dir, 'objects') + '\n')
Shawn O. Pearce88443382010-10-08 10:02:09 +02002330
Kevin Degib1a07b82015-07-27 13:33:43 -06002331 self._UpdateHooks()
Jimmie Westera0444582012-10-24 13:44:42 +02002332
Kevin Degib1a07b82015-07-27 13:33:43 -06002333 m = self.manifest.manifestProject.config
2334 for key in ['user.name', 'user.email']:
2335 if m.Has(key, include_defaults=False):
2336 self.config.SetString(key, m.GetString(key))
David Pursehouse76a4a9d2016-08-16 12:11:12 +09002337 self.config.SetString('filter.lfs.smudge', 'git-lfs smudge --skip -- %f')
Kevin Degib1a07b82015-07-27 13:33:43 -06002338 if self.manifest.IsMirror:
2339 self.config.SetString('core.bare', 'true')
2340 else:
2341 self.config.SetString('core.bare', None)
2342 except Exception:
2343 if init_obj_dir and os.path.exists(self.objdir):
2344 shutil.rmtree(self.objdir)
2345 if init_git_dir and os.path.exists(self.gitdir):
2346 shutil.rmtree(self.gitdir)
2347 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002348
Jimmie Westera0444582012-10-24 13:44:42 +02002349 def _UpdateHooks(self):
2350 if os.path.exists(self.gitdir):
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002351 self._InitHooks()
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002352
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002353 def _InitHooks(self):
Jesse Hall672cc492013-11-27 11:17:13 -08002354 hooks = os.path.realpath(self._gitdir_path('hooks'))
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002355 if not os.path.exists(hooks):
2356 os.makedirs(hooks)
Jonathan Nieder93719792015-03-17 11:29:58 -07002357 for stock_hook in _ProjectHooks():
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002358 name = os.path.basename(stock_hook)
2359
Victor Boivie65e0f352011-04-18 11:23:29 +02002360 if name in ('commit-msg',) and not self.remote.review \
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002361 and self is not self.manifest.manifestProject:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002362 # Don't install a Gerrit Code Review hook if this
2363 # project does not appear to use it for reviews.
2364 #
Victor Boivie65e0f352011-04-18 11:23:29 +02002365 # Since the manifest project is one of those, but also
2366 # managed through gerrit, it's excluded
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002367 continue
2368
2369 dst = os.path.join(hooks, name)
2370 if os.path.islink(dst):
2371 continue
2372 if os.path.exists(dst):
2373 if filecmp.cmp(stock_hook, dst, shallow=False):
2374 os.remove(dst)
2375 else:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002376 _warn("%s: Not replacing locally modified %s hook",
2377 self.relpath, name)
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002378 continue
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002379 try:
Mickaël Salaünb9477bc2012-08-05 13:39:26 +02002380 os.symlink(os.path.relpath(stock_hook, os.path.dirname(dst)), dst)
Sarah Owensa5be53f2012-09-09 15:37:57 -07002381 except OSError as e:
Shawn O. Pearce9452e4e2009-08-22 18:17:46 -07002382 if e.errno == errno.EPERM:
Shawn O. Pearcec9ef7442008-11-03 10:32:09 -08002383 raise GitError('filesystem must support symlinks')
2384 else:
2385 raise
2386
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002387 def _InitRemote(self):
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002388 if self.remote.url:
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002389 remote = self.GetRemote(self.remote.name)
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002390 remote.url = self.remote.url
Steve Raed6480452016-08-10 15:00:00 -07002391 remote.pushUrl = self.remote.pushUrl
Shawn O. Pearced1f70d92009-05-19 14:58:02 -07002392 remote.review = self.remote.review
2393 remote.projectname = self.name
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002394
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002395 if self.worktree:
2396 remote.ResetFetch(mirror=False)
2397 else:
2398 remote.ResetFetch(mirror=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002399 remote.Save()
2400
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002401 def _InitMRef(self):
2402 if self.manifest.branch:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002403 self._InitAnyMRef(R_M + self.manifest.branch)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002404
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002405 def _InitMirrorHead(self):
Shawn O. Pearcefe200ee2009-06-01 15:28:21 -07002406 self._InitAnyMRef(HEAD)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002407
2408 def _InitAnyMRef(self, ref):
2409 cur = self.bare_ref.symref(ref)
2410
2411 if self.revisionId:
2412 if cur != '' or self.bare_ref.get(ref) != self.revisionId:
2413 msg = 'manifest set to %s' % self.revisionId
2414 dst = self.revisionId + '^0'
Anthony King7bdac712014-07-16 12:56:40 +01002415 self.bare_git.UpdateRef(ref, dst, message=msg, detach=True)
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002416 else:
2417 remote = self.GetRemote(self.remote.name)
2418 dst = remote.ToLocal(self.revisionExpr)
2419 if cur != dst:
2420 msg = 'manifest set to %s' % self.revisionExpr
2421 self.bare_git.symbolic_ref('-m', msg, ref, dst)
Shawn O. Pearcee284ad12008-11-04 07:37:10 -08002422
Kevin Degi384b3c52014-10-16 16:02:58 -06002423 def _CheckDirReference(self, srcdir, destdir, share_refs):
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002424 symlink_files = self.shareable_files[:]
2425 symlink_dirs = self.shareable_dirs[:]
Kevin Degi384b3c52014-10-16 16:02:58 -06002426 if share_refs:
2427 symlink_files += self.working_tree_files
2428 symlink_dirs += self.working_tree_dirs
2429 to_symlink = symlink_files + symlink_dirs
2430 for name in set(to_symlink):
2431 dst = os.path.realpath(os.path.join(destdir, name))
2432 if os.path.lexists(dst):
2433 src = os.path.realpath(os.path.join(srcdir, name))
2434 # Fail if the links are pointing to the wrong place
2435 if src != dst:
Marc Herbertec287902016-10-27 12:58:26 -07002436 _error('%s is different in %s vs %s', name, destdir, srcdir)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002437 raise GitError('--force-sync not enabled; cannot overwrite a local '
Simon Ruggierf9b76832015-07-31 17:18:34 -04002438 'work tree. If you\'re comfortable with the '
2439 'possibility of losing the work tree\'s git metadata,'
2440 ' use `repo sync --force-sync {0}` to '
2441 'proceed.'.format(self.relpath))
Kevin Degi384b3c52014-10-16 16:02:58 -06002442
David James8d201162013-10-11 17:03:19 -07002443 def _ReferenceGitDir(self, gitdir, dotgit, share_refs, copy_all):
2444 """Update |dotgit| to reference |gitdir|, using symlinks where possible.
2445
2446 Args:
2447 gitdir: The bare git repository. Must already be initialized.
2448 dotgit: The repository you would like to initialize.
2449 share_refs: If true, |dotgit| will store its refs under |gitdir|.
2450 Only one work tree can store refs under a given |gitdir|.
2451 copy_all: If true, copy all remaining files from |gitdir| -> |dotgit|.
2452 This saves you the effort of initializing |dotgit| yourself.
2453 """
Dan Willemsenbdb866e2016-04-05 17:22:02 -07002454 symlink_files = self.shareable_files[:]
2455 symlink_dirs = self.shareable_dirs[:]
David James8d201162013-10-11 17:03:19 -07002456 if share_refs:
Kevin Degi384b3c52014-10-16 16:02:58 -06002457 symlink_files += self.working_tree_files
2458 symlink_dirs += self.working_tree_dirs
David James8d201162013-10-11 17:03:19 -07002459 to_symlink = symlink_files + symlink_dirs
2460
2461 to_copy = []
2462 if copy_all:
2463 to_copy = os.listdir(gitdir)
2464
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002465 dotgit = os.path.realpath(dotgit)
David James8d201162013-10-11 17:03:19 -07002466 for name in set(to_copy).union(to_symlink):
2467 try:
2468 src = os.path.realpath(os.path.join(gitdir, name))
Dan Willemsen2a3e1522015-07-30 20:43:33 -07002469 dst = os.path.join(dotgit, name)
David James8d201162013-10-11 17:03:19 -07002470
Kevin Degi384b3c52014-10-16 16:02:58 -06002471 if os.path.lexists(dst):
2472 continue
David James8d201162013-10-11 17:03:19 -07002473
2474 # If the source dir doesn't exist, create an empty dir.
2475 if name in symlink_dirs and not os.path.lexists(src):
2476 os.makedirs(src)
2477
Cheuk Leung8ac0c962015-07-06 21:33:00 +02002478 if name in to_symlink:
2479 os.symlink(os.path.relpath(src, os.path.dirname(dst)), dst)
2480 elif copy_all and not os.path.islink(dst):
2481 if os.path.isdir(src):
2482 shutil.copytree(src, dst)
2483 elif os.path.isfile(src):
2484 shutil.copy(src, dst)
2485
Conley Owens80b87fe2014-05-09 17:13:44 -07002486 # If the source file doesn't exist, ensure the destination
2487 # file doesn't either.
2488 if name in symlink_files and not os.path.lexists(src):
2489 try:
2490 os.remove(dst)
2491 except OSError:
2492 pass
2493
David James8d201162013-10-11 17:03:19 -07002494 except OSError as e:
2495 if e.errno == errno.EPERM:
Kevin Degiabaa7f32014-11-12 11:27:45 -07002496 raise DownloadError('filesystem must support symlinks')
David James8d201162013-10-11 17:03:19 -07002497 else:
2498 raise
2499
Martin Kellye4e94d22017-03-21 16:05:12 -07002500 def _InitWorkTree(self, force_sync=False, submodules=False):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002501 dotgit = os.path.join(self.worktree, '.git')
Kevin Degi384b3c52014-10-16 16:02:58 -06002502 init_dotgit = not os.path.exists(dotgit)
Kevin Degib1a07b82015-07-27 13:33:43 -06002503 try:
2504 if init_dotgit:
2505 os.makedirs(dotgit)
2506 self._ReferenceGitDir(self.gitdir, dotgit, share_refs=True,
2507 copy_all=False)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002508
Kevin Degiabaa7f32014-11-12 11:27:45 -07002509 try:
2510 self._CheckDirReference(self.gitdir, dotgit, share_refs=True)
2511 except GitError as e:
2512 if force_sync:
2513 try:
2514 shutil.rmtree(dotgit)
Martin Kellye4e94d22017-03-21 16:05:12 -07002515 return self._InitWorkTree(force_sync=False, submodules=submodules)
Kevin Degiabaa7f32014-11-12 11:27:45 -07002516 except:
2517 raise e
2518 raise e
Kevin Degi384b3c52014-10-16 16:02:58 -06002519
Kevin Degib1a07b82015-07-27 13:33:43 -06002520 if init_dotgit:
2521 _lwrite(os.path.join(dotgit, HEAD), '%s\n' % self.GetRevisionId())
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002522
Kevin Degib1a07b82015-07-27 13:33:43 -06002523 cmd = ['read-tree', '--reset', '-u']
2524 cmd.append('-v')
2525 cmd.append(HEAD)
2526 if GitCommand(self, cmd).Wait() != 0:
2527 raise GitError("cannot initialize work tree")
Victor Boivie0960b5b2010-11-26 13:42:13 +01002528
Martin Kellye4e94d22017-03-21 16:05:12 -07002529 if submodules:
2530 self._SyncSubmodules(quiet=True)
Kevin Degib1a07b82015-07-27 13:33:43 -06002531 self._CopyAndLinkFiles()
2532 except Exception:
2533 if init_dotgit:
2534 shutil.rmtree(dotgit)
2535 raise
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002536
2537 def _gitdir_path(self, path):
David James8d201162013-10-11 17:03:19 -07002538 return os.path.realpath(os.path.join(self.gitdir, path))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002539
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002540 def _revlist(self, *args, **kw):
2541 a = []
2542 a.extend(args)
2543 a.append('--')
2544 return self.work_git.rev_list(*a, **kw)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002545
2546 @property
2547 def _allrefs(self):
Shawn O. Pearced237b692009-04-17 18:49:50 -07002548 return self.bare_ref.all
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002549
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002550 def _getLogs(self, rev1, rev2, oneline=False, color=True, pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002551 """Get logs between two revisions of this project."""
2552 comp = '..'
2553 if rev1:
2554 revs = [rev1]
2555 if rev2:
2556 revs.extend([comp, rev2])
2557 cmd = ['log', ''.join(revs)]
2558 out = DiffColoring(self.config)
2559 if out.is_on and color:
2560 cmd.append('--color')
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002561 if pretty_format is not None:
2562 cmd.append('--pretty=format:%s' % pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002563 if oneline:
2564 cmd.append('--oneline')
2565
2566 try:
2567 log = GitCommand(self, cmd, capture_stdout=True, capture_stderr=True)
2568 if log.Wait() == 0:
2569 return log.stdout
2570 except GitError:
2571 # worktree may not exist if groups changed for example. In that case,
2572 # try in gitdir instead.
2573 if not os.path.exists(self.worktree):
2574 return self.bare_git.log(*cmd[1:])
2575 else:
2576 raise
2577 return None
2578
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002579 def getAddedAndRemovedLogs(self, toProject, oneline=False, color=True,
2580 pretty_format=None):
Julien Camperguedd654222014-01-09 16:21:37 +01002581 """Get the list of logs from this revision to given revisionId"""
2582 logs = {}
2583 selfId = self.GetRevisionId(self._allrefs)
2584 toId = toProject.GetRevisionId(toProject._allrefs)
2585
Sebastian Schuberth7ecccf62016-03-29 14:11:20 +02002586 logs['added'] = self._getLogs(selfId, toId, oneline=oneline, color=color,
2587 pretty_format=pretty_format)
2588 logs['removed'] = self._getLogs(toId, selfId, oneline=oneline, color=color,
2589 pretty_format=pretty_format)
Julien Camperguedd654222014-01-09 16:21:37 +01002590 return logs
2591
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002592 class _GitGetByExec(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002593
David James8d201162013-10-11 17:03:19 -07002594 def __init__(self, project, bare, gitdir):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002595 self._project = project
2596 self._bare = bare
David James8d201162013-10-11 17:03:19 -07002597 self._gitdir = gitdir
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002598
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002599 def LsOthers(self):
2600 p = GitCommand(self._project,
2601 ['ls-files',
2602 '-z',
2603 '--others',
2604 '--exclude-standard'],
Anthony King7bdac712014-07-16 12:56:40 +01002605 bare=False,
David James8d201162013-10-11 17:03:19 -07002606 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002607 capture_stdout=True,
2608 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002609 if p.Wait() == 0:
2610 out = p.stdout
2611 if out:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002612 # Backslash is not anomalous
David Pursehouse1d947b32012-10-25 12:23:11 +09002613 return out[:-1].split('\0') # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002614 return []
2615
2616 def DiffZ(self, name, *args):
2617 cmd = [name]
2618 cmd.append('-z')
2619 cmd.extend(args)
2620 p = GitCommand(self._project,
2621 cmd,
David James8d201162013-10-11 17:03:19 -07002622 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002623 bare=False,
2624 capture_stdout=True,
2625 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002626 try:
2627 out = p.process.stdout.read()
2628 r = {}
2629 if out:
David Pursehouse1d947b32012-10-25 12:23:11 +09002630 out = iter(out[:-1].split('\0')) # pylint: disable=W1401
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002631 while out:
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002632 try:
Anthony King2cd1f042014-05-05 21:24:05 +01002633 info = next(out)
2634 path = next(out)
Shawn O. Pearce02dbb6d2008-10-21 13:59:08 -07002635 except StopIteration:
2636 break
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002637
2638 class _Info(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002639
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002640 def __init__(self, path, omode, nmode, oid, nid, state):
2641 self.path = path
2642 self.src_path = None
2643 self.old_mode = omode
2644 self.new_mode = nmode
2645 self.old_id = oid
2646 self.new_id = nid
2647
2648 if len(state) == 1:
2649 self.status = state
2650 self.level = None
2651 else:
2652 self.status = state[:1]
2653 self.level = state[1:]
2654 while self.level.startswith('0'):
2655 self.level = self.level[1:]
2656
2657 info = info[1:].split(' ')
David Pursehouse8f62fb72012-11-14 12:09:38 +09002658 info = _Info(path, *info)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002659 if info.status in ('R', 'C'):
2660 info.src_path = info.path
Anthony King2cd1f042014-05-05 21:24:05 +01002661 info.path = next(out)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002662 r[info.path] = info
2663 return r
2664 finally:
2665 p.Wait()
2666
2667 def GetHead(self):
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002668 if self._bare:
2669 path = os.path.join(self._project.gitdir, HEAD)
2670 else:
2671 path = os.path.join(self._project.worktree, '.git', HEAD)
Conley Owens75ee0572012-11-15 17:33:11 -08002672 try:
2673 fd = open(path, 'rb')
Dan Sandler53e902a2014-03-09 13:20:02 -04002674 except IOError as e:
2675 raise NoManifestException(path, str(e))
Shawn O. Pearce76ca9f82009-04-18 14:48:03 -07002676 try:
2677 line = fd.read()
2678 finally:
2679 fd.close()
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302680 try:
2681 line = line.decode()
2682 except AttributeError:
2683 pass
Shawn O. Pearce5b23f242009-04-17 18:43:33 -07002684 if line.startswith('ref: '):
2685 return line[5:-1]
2686 return line[:-1]
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002687
2688 def SetHead(self, ref, message=None):
2689 cmdv = []
2690 if message is not None:
2691 cmdv.extend(['-m', message])
2692 cmdv.append(HEAD)
2693 cmdv.append(ref)
2694 self.symbolic_ref(*cmdv)
2695
2696 def DetachHead(self, new, message=None):
2697 cmdv = ['--no-deref']
2698 if message is not None:
2699 cmdv.extend(['-m', message])
2700 cmdv.append(HEAD)
2701 cmdv.append(new)
2702 self.update_ref(*cmdv)
2703
2704 def UpdateRef(self, name, new, old=None,
2705 message=None,
2706 detach=False):
2707 cmdv = []
2708 if message is not None:
2709 cmdv.extend(['-m', message])
2710 if detach:
2711 cmdv.append('--no-deref')
2712 cmdv.append(name)
2713 cmdv.append(new)
2714 if old is not None:
2715 cmdv.append(old)
2716 self.update_ref(*cmdv)
2717
2718 def DeleteRef(self, name, old=None):
2719 if not old:
2720 old = self.rev_parse(name)
2721 self.update_ref('-d', name, old)
Shawn O. Pearcefbcde472009-04-17 20:58:02 -07002722 self._project.bare_ref.deleted(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002723
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002724 def rev_list(self, *args, **kw):
2725 if 'format' in kw:
2726 cmdv = ['log', '--pretty=format:%s' % kw['format']]
2727 else:
2728 cmdv = ['rev-list']
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002729 cmdv.extend(args)
2730 p = GitCommand(self._project,
2731 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002732 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002733 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002734 capture_stdout=True,
2735 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002736 r = []
2737 for line in p.process.stdout:
Shawn O. Pearce8ad8a0e2009-05-29 18:28:25 -07002738 if line[-1] == '\n':
2739 line = line[:-1]
2740 r.append(line)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002741 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002742 raise GitError('%s rev-list %s: %s' %
2743 (self._project.name, str(args), p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002744 return r
2745
2746 def __getattr__(self, name):
Doug Anderson37282b42011-03-04 11:54:18 -08002747 """Allow arbitrary git commands using pythonic syntax.
2748
2749 This allows you to do things like:
2750 git_obj.rev_parse('HEAD')
2751
2752 Since we don't have a 'rev_parse' method defined, the __getattr__ will
2753 run. We'll replace the '_' with a '-' and try to run a git command.
Dave Borowitz091f8932012-10-23 17:01:04 -07002754 Any other positional arguments will be passed to the git command, and the
2755 following keyword arguments are supported:
2756 config: An optional dict of git config options to be passed with '-c'.
Doug Anderson37282b42011-03-04 11:54:18 -08002757
2758 Args:
2759 name: The name of the git command to call. Any '_' characters will
2760 be replaced with '-'.
2761
2762 Returns:
2763 A callable object that will try to call git with the named command.
2764 """
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002765 name = name.replace('_', '-')
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002766
Dave Borowitz091f8932012-10-23 17:01:04 -07002767 def runner(*args, **kwargs):
2768 cmdv = []
2769 config = kwargs.pop('config', None)
2770 for k in kwargs:
2771 raise TypeError('%s() got an unexpected keyword argument %r'
2772 % (name, k))
2773 if config is not None:
Dave Borowitzb42b4742012-10-31 12:27:27 -07002774 if not git_require((1, 7, 2)):
2775 raise ValueError('cannot set config on command line for %s()'
2776 % name)
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302777 for k, v in config.items():
Dave Borowitz091f8932012-10-23 17:01:04 -07002778 cmdv.append('-c')
2779 cmdv.append('%s=%s' % (k, v))
2780 cmdv.append(name)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002781 cmdv.extend(args)
2782 p = GitCommand(self._project,
2783 cmdv,
Anthony King7bdac712014-07-16 12:56:40 +01002784 bare=self._bare,
David James8d201162013-10-11 17:03:19 -07002785 gitdir=self._gitdir,
Anthony King7bdac712014-07-16 12:56:40 +01002786 capture_stdout=True,
2787 capture_stderr=True)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002788 if p.Wait() != 0:
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002789 raise GitError('%s %s: %s' %
2790 (self._project.name, name, p.stderr))
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002791 r = p.stdout
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302792 try:
Conley Owensedd01512013-09-26 12:59:58 -07002793 r = r.decode('utf-8')
Chirayu Desai217ea7d2013-03-01 19:14:38 +05302794 except AttributeError:
2795 pass
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002796 if r.endswith('\n') and r.index('\n') == len(r) - 1:
2797 return r[:-1]
2798 return r
2799 return runner
2800
2801
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002802class _PriorSyncFailedError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002803
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002804 def __str__(self):
2805 return 'prior sync failed; rebase still in progress'
2806
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002807
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002808class _DirtyError(Exception):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002809
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002810 def __str__(self):
2811 return 'contains uncommitted changes'
2812
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002813
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002814class _InfoMessage(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002815
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002816 def __init__(self, project, text):
2817 self.project = project
2818 self.text = text
2819
2820 def Print(self, syncbuf):
2821 syncbuf.out.info('%s/: %s', self.project.relpath, self.text)
2822 syncbuf.out.nl()
2823
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002824
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002825class _Failure(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002826
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002827 def __init__(self, project, why):
2828 self.project = project
2829 self.why = why
2830
2831 def Print(self, syncbuf):
2832 syncbuf.out.fail('error: %s/: %s',
2833 self.project.relpath,
2834 str(self.why))
2835 syncbuf.out.nl()
2836
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002837
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002838class _Later(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002839
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002840 def __init__(self, project, action):
2841 self.project = project
2842 self.action = action
2843
2844 def Run(self, syncbuf):
2845 out = syncbuf.out
2846 out.project('project %s/', self.project.relpath)
2847 out.nl()
2848 try:
2849 self.action()
2850 out.nl()
2851 return True
David Pursehouse8a68ff92012-09-24 12:15:13 +09002852 except GitError:
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002853 out.nl()
2854 return False
2855
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002856
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002857class _SyncColoring(Coloring):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002858
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002859 def __init__(self, config):
2860 Coloring.__init__(self, config, 'reposync')
Anthony King7bdac712014-07-16 12:56:40 +01002861 self.project = self.printer('header', attr='bold')
2862 self.info = self.printer('info')
2863 self.fail = self.printer('fail', fg='red')
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002864
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002865
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002866class SyncBuffer(object):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002867
Shawn O. Pearce350cde42009-04-16 11:21:18 -07002868 def __init__(self, config, detach_head=False):
2869 self._messages = []
2870 self._failures = []
2871 self._later_queue1 = []
2872 self._later_queue2 = []
2873
2874 self.out = _SyncColoring(config)
2875 self.out.redirect(sys.stderr)
2876
2877 self.detach_head = detach_head
2878 self.clean = True
2879
2880 def info(self, project, fmt, *args):
2881 self._messages.append(_InfoMessage(project, fmt % args))
2882
2883 def fail(self, project, err=None):
2884 self._failures.append(_Failure(project, err))
2885 self.clean = False
2886
2887 def later1(self, project, what):
2888 self._later_queue1.append(_Later(project, what))
2889
2890 def later2(self, project, what):
2891 self._later_queue2.append(_Later(project, what))
2892
2893 def Finish(self):
2894 self._PrintMessages()
2895 self._RunLater()
2896 self._PrintMessages()
2897 return self.clean
2898
2899 def _RunLater(self):
2900 for q in ['_later_queue1', '_later_queue2']:
2901 if not self._RunQueue(q):
2902 return
2903
2904 def _RunQueue(self, queue):
2905 for m in getattr(self, queue):
2906 if not m.Run(self):
2907 self.clean = False
2908 return False
2909 setattr(self, queue, [])
2910 return True
2911
2912 def _PrintMessages(self):
2913 for m in self._messages:
2914 m.Print(self)
2915 for m in self._failures:
2916 m.Print(self)
2917
2918 self._messages = []
2919 self._failures = []
2920
2921
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002922class MetaProject(Project):
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002923
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002924 """A special project housed under .repo.
2925 """
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002926
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002927 def __init__(self, manifest, name, gitdir, worktree):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002928 Project.__init__(self,
Anthony King7bdac712014-07-16 12:56:40 +01002929 manifest=manifest,
2930 name=name,
2931 gitdir=gitdir,
2932 objdir=gitdir,
2933 worktree=worktree,
2934 remote=RemoteSpec('origin'),
2935 relpath='.repo/%s' % name,
2936 revisionExpr='refs/heads/master',
2937 revisionId=None,
2938 groups=None)
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002939
2940 def PreSync(self):
2941 if self.Exists:
2942 cb = self.CurrentBranch
2943 if cb:
2944 base = self.GetBranch(cb).merge
2945 if base:
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002946 self.revisionExpr = base
2947 self.revisionId = None
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002948
Anthony King7bdac712014-07-16 12:56:40 +01002949 def MetaBranchSwitch(self):
Florian Vallee5d016502012-06-07 17:19:26 +02002950 """ Prepare MetaProject for manifest branch switch
2951 """
2952
2953 # detach and delete manifest branch, allowing a new
2954 # branch to take over
Anthony King7bdac712014-07-16 12:56:40 +01002955 syncbuf = SyncBuffer(self.config, detach_head=True)
Florian Vallee5d016502012-06-07 17:19:26 +02002956 self.Sync_LocalHalf(syncbuf)
2957 syncbuf.Finish()
2958
2959 return GitCommand(self,
Mark E. Hamilton30b0f4e2016-02-10 10:44:30 -07002960 ['update-ref', '-d', 'refs/heads/default'],
2961 capture_stdout=True,
2962 capture_stderr=True).Wait() == 0
Florian Vallee5d016502012-06-07 17:19:26 +02002963
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002964 @property
Shawn O. Pearcef6906872009-04-18 10:49:00 -07002965 def LastFetch(self):
2966 try:
2967 fh = os.path.join(self.gitdir, 'FETCH_HEAD')
2968 return os.path.getmtime(fh)
2969 except OSError:
2970 return 0
2971
2972 @property
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002973 def HasChanges(self):
2974 """Has the remote received new commits not yet checked out?
2975 """
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002976 if not self.remote or not self.revisionExpr:
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07002977 return False
2978
David Pursehouse8a68ff92012-09-24 12:15:13 +09002979 all_refs = self.bare_ref.all
2980 revid = self.GetRevisionId(all_refs)
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07002981 head = self.work_git.GetHead()
2982 if head.startswith(R_HEADS):
2983 try:
David Pursehouse8a68ff92012-09-24 12:15:13 +09002984 head = all_refs[head]
Shawn O. Pearce336f7bd2009-04-18 10:39:28 -07002985 except KeyError:
2986 head = None
2987
2988 if revid == head:
2989 return False
Shawn O. Pearce3c8dea12009-05-29 18:38:17 -07002990 elif self._revlist(not_rev(HEAD), revid):
The Android Open Source Projectcf31fe92008-10-21 07:00:00 -07002991 return True
2992 return False