blob: 7b618c6fb1f0424a0240ac835e38e3715d82d3ef [file] [log] [blame]
Patrick Williamsc124f4f2015-09-15 14:41:29 -05001# ex:ts=4:sw=4:sts=4:et
2# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
3"""
4BitBake 'Fetch' git implementation
5
6git fetcher support the SRC_URI with format of:
7SRC_URI = "git://some.host/somepath;OptionA=xxx;OptionB=xxx;..."
8
9Supported SRC_URI options are:
10
11- branch
12 The git branch to retrieve from. The default is "master"
13
14 This option also supports multiple branch fetching, with branches
15 separated by commas. In multiple branches case, the name option
16 must have the same number of names to match the branches, which is
17 used to specify the SRC_REV for the branch
18 e.g:
19 SRC_URI="git://some.host/somepath;branch=branchX,branchY;name=nameX,nameY"
20 SRCREV_nameX = "xxxxxxxxxxxxxxxxxxxx"
21 SRCREV_nameY = "YYYYYYYYYYYYYYYYYYYY"
22
23- tag
24 The git tag to retrieve. The default is "master"
25
26- protocol
27 The method to use to access the repository. Common options are "git",
28 "http", "https", "file", "ssh" and "rsync". The default is "git".
29
30- rebaseable
31 rebaseable indicates that the upstream git repo may rebase in the future,
32 and current revision may disappear from upstream repo. This option will
33 remind fetcher to preserve local cache carefully for future use.
34 The default value is "0", set rebaseable=1 for rebaseable git repo.
35
36- nocheckout
37 Don't checkout source code when unpacking. set this option for the recipe
38 who has its own routine to checkout code.
39 The default is "0", set nocheckout=1 if needed.
40
41- bareclone
42 Create a bare clone of the source code and don't checkout the source code
43 when unpacking. Set this option for the recipe who has its own routine to
44 checkout code and tracking branch requirements.
45 The default is "0", set bareclone=1 if needed.
46
47- nobranch
48 Don't check the SHA validation for branch. set this option for the recipe
49 referring to commit which is valid in tag instead of branch.
50 The default is "0", set nobranch=1 if needed.
51
Patrick Williamsc0f7c042017-02-23 20:41:17 -060052- usehead
Brad Bishop6e60e8b2018-02-01 10:27:11 -050053 For local git:// urls to use the current branch HEAD as the revision for use with
Patrick Williamsc0f7c042017-02-23 20:41:17 -060054 AUTOREV. Implies nobranch.
55
Patrick Williamsc124f4f2015-09-15 14:41:29 -050056"""
57
58#Copyright (C) 2005 Richard Purdie
59#
60# This program is free software; you can redistribute it and/or modify
61# it under the terms of the GNU General Public License version 2 as
62# published by the Free Software Foundation.
63#
64# This program is distributed in the hope that it will be useful,
65# but WITHOUT ANY WARRANTY; without even the implied warranty of
66# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
67# GNU General Public License for more details.
68#
69# You should have received a copy of the GNU General Public License along
70# with this program; if not, write to the Free Software Foundation, Inc.,
71# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
72
Brad Bishopd7bf8c12018-02-25 22:55:05 -050073import collections
Patrick Williamsd7e96312015-09-22 08:09:05 -050074import errno
Brad Bishopd7bf8c12018-02-25 22:55:05 -050075import fnmatch
Patrick Williamsc124f4f2015-09-15 14:41:29 -050076import os
77import re
Brad Bishopd7bf8c12018-02-25 22:55:05 -050078import subprocess
79import tempfile
Patrick Williamsc124f4f2015-09-15 14:41:29 -050080import bb
Patrick Williamsc0f7c042017-02-23 20:41:17 -060081import bb.progress
Patrick Williamsc124f4f2015-09-15 14:41:29 -050082from bb.fetch2 import FetchMethod
83from bb.fetch2 import runfetchcmd
84from bb.fetch2 import logger
85
Patrick Williamsc0f7c042017-02-23 20:41:17 -060086
87class GitProgressHandler(bb.progress.LineFilterProgressHandler):
88 """Extract progress information from git output"""
89 def __init__(self, d):
90 self._buffer = ''
91 self._count = 0
92 super(GitProgressHandler, self).__init__(d)
93 # Send an initial progress event so the bar gets shown
94 self._fire_progress(-1)
95
96 def write(self, string):
97 self._buffer += string
98 stages = ['Counting objects', 'Compressing objects', 'Receiving objects', 'Resolving deltas']
99 stage_weights = [0.2, 0.05, 0.5, 0.25]
100 stagenum = 0
101 for i, stage in reversed(list(enumerate(stages))):
102 if stage in self._buffer:
103 stagenum = i
104 self._buffer = ''
105 break
106 self._status = stages[stagenum]
107 percs = re.findall(r'(\d+)%', string)
108 if percs:
109 progress = int(round((int(percs[-1]) * stage_weights[stagenum]) + (sum(stage_weights[:stagenum]) * 100)))
110 rates = re.findall(r'([\d.]+ [a-zA-Z]*/s+)', string)
111 if rates:
112 rate = rates[-1]
113 else:
114 rate = None
115 self.update(progress, rate)
116 else:
117 if stagenum == 0:
118 percs = re.findall(r': (\d+)', string)
119 if percs:
120 count = int(percs[-1])
121 if count > self._count:
122 self._count = count
123 self._fire_progress(-count)
124 super(GitProgressHandler, self).write(string)
125
126
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500127class Git(FetchMethod):
Brad Bishop316dfdd2018-06-25 12:45:53 -0400128 bitbake_dir = os.path.abspath(os.path.join(os.path.dirname(os.path.join(os.path.abspath(__file__))), '..', '..', '..'))
129 make_shallow_path = os.path.join(bitbake_dir, 'bin', 'git-make-shallow')
130
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500131 """Class to fetch a module or modules from git repositories"""
132 def init(self, d):
133 pass
134
135 def supports(self, ud, d):
136 """
137 Check to see if a given url can be fetched with git.
138 """
139 return ud.type in ['git']
140
141 def supports_checksum(self, urldata):
142 return False
143
144 def urldata_init(self, ud, d):
145 """
146 init git specific variable within url data
147 so that the git method like latest_revision() can work
148 """
149 if 'protocol' in ud.parm:
150 ud.proto = ud.parm['protocol']
151 elif not ud.host:
152 ud.proto = 'file'
153 else:
154 ud.proto = "git"
155
156 if not ud.proto in ('git', 'file', 'ssh', 'http', 'https', 'rsync'):
157 raise bb.fetch2.ParameterError("Invalid protocol type", ud.url)
158
159 ud.nocheckout = ud.parm.get("nocheckout","0") == "1"
160
161 ud.rebaseable = ud.parm.get("rebaseable","0") == "1"
162
163 ud.nobranch = ud.parm.get("nobranch","0") == "1"
164
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600165 # usehead implies nobranch
166 ud.usehead = ud.parm.get("usehead","0") == "1"
167 if ud.usehead:
168 if ud.proto != "file":
169 raise bb.fetch2.ParameterError("The usehead option is only for use with local ('protocol=file') git repositories", ud.url)
170 ud.nobranch = 1
171
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500172 # bareclone implies nocheckout
173 ud.bareclone = ud.parm.get("bareclone","0") == "1"
174 if ud.bareclone:
175 ud.nocheckout = 1
176
177 ud.unresolvedrev = {}
178 branches = ud.parm.get("branch", "master").split(',')
179 if len(branches) != len(ud.names):
180 raise bb.fetch2.ParameterError("The number of name and branch parameters is not balanced", ud.url)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500181
182 ud.cloneflags = "-s -n"
183 if ud.bareclone:
184 ud.cloneflags += " --mirror"
185
186 ud.shallow = d.getVar("BB_GIT_SHALLOW") == "1"
187 ud.shallow_extra_refs = (d.getVar("BB_GIT_SHALLOW_EXTRA_REFS") or "").split()
188
189 depth_default = d.getVar("BB_GIT_SHALLOW_DEPTH")
190 if depth_default is not None:
191 try:
192 depth_default = int(depth_default or 0)
193 except ValueError:
194 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
195 else:
196 if depth_default < 0:
197 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH: %s" % depth_default)
198 else:
199 depth_default = 1
200 ud.shallow_depths = collections.defaultdict(lambda: depth_default)
201
202 revs_default = d.getVar("BB_GIT_SHALLOW_REVS", True)
203 ud.shallow_revs = []
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500204 ud.branches = {}
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500205 for pos, name in enumerate(ud.names):
206 branch = branches[pos]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500207 ud.branches[name] = branch
208 ud.unresolvedrev[name] = branch
209
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500210 shallow_depth = d.getVar("BB_GIT_SHALLOW_DEPTH_%s" % name)
211 if shallow_depth is not None:
212 try:
213 shallow_depth = int(shallow_depth or 0)
214 except ValueError:
215 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
216 else:
217 if shallow_depth < 0:
218 raise bb.fetch2.FetchError("Invalid depth for BB_GIT_SHALLOW_DEPTH_%s: %s" % (name, shallow_depth))
219 ud.shallow_depths[name] = shallow_depth
220
221 revs = d.getVar("BB_GIT_SHALLOW_REVS_%s" % name)
222 if revs is not None:
223 ud.shallow_revs.extend(revs.split())
224 elif revs_default is not None:
225 ud.shallow_revs.extend(revs_default.split())
226
227 if (ud.shallow and
228 not ud.shallow_revs and
229 all(ud.shallow_depths[n] == 0 for n in ud.names)):
230 # Shallow disabled for this URL
231 ud.shallow = False
232
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600233 if ud.usehead:
234 ud.unresolvedrev['default'] = 'HEAD'
235
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500236 ud.basecmd = d.getVar("FETCHCMD_git") or "git -c core.fsyncobjectfiles=0"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500237
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500238 write_tarballs = d.getVar("BB_GENERATE_MIRROR_TARBALLS") or "0"
239 ud.write_tarballs = write_tarballs != "0" or ud.rebaseable
240 ud.write_shallow_tarballs = (d.getVar("BB_GENERATE_SHALLOW_TARBALLS") or write_tarballs) != "0"
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500241
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500242 ud.setup_revisions(d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500243
244 for name in ud.names:
245 # Ensure anything that doesn't look like a sha256 checksum/revision is translated into one
246 if not ud.revisions[name] or len(ud.revisions[name]) != 40 or (False in [c in "abcdef0123456789" for c in ud.revisions[name]]):
247 if ud.revisions[name]:
248 ud.unresolvedrev[name] = ud.revisions[name]
249 ud.revisions[name] = self.latest_revision(ud, d, name)
250
251 gitsrcname = '%s%s' % (ud.host.replace(':', '.'), ud.path.replace('/', '.').replace('*', '.'))
252 if gitsrcname.startswith('.'):
253 gitsrcname = gitsrcname[1:]
254
255 # for rebaseable git repo, it is necessary to keep mirror tar ball
256 # per revision, so that even the revision disappears from the
257 # upstream repo in the future, the mirror will remain intact and still
258 # contains the revision
259 if ud.rebaseable:
260 for name in ud.names:
261 gitsrcname = gitsrcname + '_' + ud.revisions[name]
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500262
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500263 dl_dir = d.getVar("DL_DIR")
264 gitdir = d.getVar("GITDIR") or (dl_dir + "/git2/")
265 ud.clonedir = os.path.join(gitdir, gitsrcname)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500266 ud.localfile = ud.clonedir
267
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500268 mirrortarball = 'git2_%s.tar.gz' % gitsrcname
269 ud.fullmirror = os.path.join(dl_dir, mirrortarball)
270 ud.mirrortarballs = [mirrortarball]
271 if ud.shallow:
272 tarballname = gitsrcname
273 if ud.bareclone:
274 tarballname = "%s_bare" % tarballname
275
276 if ud.shallow_revs:
277 tarballname = "%s_%s" % (tarballname, "_".join(sorted(ud.shallow_revs)))
278
279 for name, revision in sorted(ud.revisions.items()):
280 tarballname = "%s_%s" % (tarballname, ud.revisions[name][:7])
281 depth = ud.shallow_depths[name]
282 if depth:
283 tarballname = "%s-%s" % (tarballname, depth)
284
285 shallow_refs = []
286 if not ud.nobranch:
287 shallow_refs.extend(ud.branches.values())
288 if ud.shallow_extra_refs:
289 shallow_refs.extend(r.replace('refs/heads/', '').replace('*', 'ALL') for r in ud.shallow_extra_refs)
290 if shallow_refs:
291 tarballname = "%s_%s" % (tarballname, "_".join(sorted(shallow_refs)).replace('/', '.'))
292
293 fetcher = self.__class__.__name__.lower()
294 ud.shallowtarball = '%sshallow_%s.tar.gz' % (fetcher, tarballname)
295 ud.fullshallow = os.path.join(dl_dir, ud.shallowtarball)
296 ud.mirrortarballs.insert(0, ud.shallowtarball)
297
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500298 def localpath(self, ud, d):
299 return ud.clonedir
300
301 def need_update(self, ud, d):
302 if not os.path.exists(ud.clonedir):
303 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500304 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600305 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500306 return True
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500307 if ud.shallow and ud.write_shallow_tarballs and not os.path.exists(ud.fullshallow):
308 return True
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500309 if ud.write_tarballs and not os.path.exists(ud.fullmirror):
310 return True
311 return False
312
313 def try_premirror(self, ud, d):
314 # If we don't do this, updating an existing checkout with only premirrors
315 # is not possible
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500316 if d.getVar("BB_FETCH_PREMIRRORONLY") is not None:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500317 return True
318 if os.path.exists(ud.clonedir):
319 return False
320 return True
321
322 def download(self, ud, d):
323 """Fetch url"""
324
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500325 no_clone = not os.path.exists(ud.clonedir)
326 need_update = no_clone or self.need_update(ud, d)
327
328 # A current clone is preferred to either tarball, a shallow tarball is
329 # preferred to an out of date clone, and a missing clone will use
330 # either tarball.
331 if ud.shallow and os.path.exists(ud.fullshallow) and need_update:
332 ud.localpath = ud.fullshallow
333 return
334 elif os.path.exists(ud.fullmirror) and no_clone:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500335 bb.utils.mkdirhier(ud.clonedir)
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500336 runfetchcmd("tar -xzf %s" % ud.fullmirror, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500337
338 repourl = self._get_repo_url(ud)
339
340 # If the repo still doesn't exist, fallback to cloning it
341 if not os.path.exists(ud.clonedir):
342 # We do this since git will use a "-l" option automatically for local urls where possible
343 if repourl.startswith("file://"):
344 repourl = repourl[7:]
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600345 clone_cmd = "LANG=C %s clone --bare --mirror %s %s --progress" % (ud.basecmd, repourl, ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500346 if ud.proto.lower() != 'file':
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500347 bb.fetch2.check_network_access(d, clone_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600348 progresshandler = GitProgressHandler(d)
349 runfetchcmd(clone_cmd, d, log=progresshandler)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500350
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500351 # Update the checkout if needed
352 needupdate = False
353 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600354 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500355 needupdate = True
356 if needupdate:
Brad Bishop6ef32652018-10-09 18:59:25 +0100357 output = runfetchcmd("%s remote" % ud.basecmd, d, quiet=True, workdir=ud.clonedir)
358 if "origin" in output:
359 runfetchcmd("%s remote rm origin" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500360
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600361 runfetchcmd("%s remote add --mirror=fetch origin %s" % (ud.basecmd, repourl), d, workdir=ud.clonedir)
362 fetch_cmd = "LANG=C %s fetch -f --prune --progress %s refs/*:refs/*" % (ud.basecmd, repourl)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500363 if ud.proto.lower() != 'file':
364 bb.fetch2.check_network_access(d, fetch_cmd, ud.url)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600365 progresshandler = GitProgressHandler(d)
366 runfetchcmd(fetch_cmd, d, log=progresshandler, workdir=ud.clonedir)
367 runfetchcmd("%s prune-packed" % ud.basecmd, d, workdir=ud.clonedir)
Brad Bishop316dfdd2018-06-25 12:45:53 -0400368 runfetchcmd("%s pack-refs --all" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600369 runfetchcmd("%s pack-redundant --all | xargs -r rm" % ud.basecmd, d, workdir=ud.clonedir)
Patrick Williamsd7e96312015-09-22 08:09:05 -0500370 try:
371 os.unlink(ud.fullmirror)
372 except OSError as exc:
373 if exc.errno != errno.ENOENT:
374 raise
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500375 for name in ud.names:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600376 if not self._contains_ref(ud, d, name, ud.clonedir):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500377 raise bb.fetch2.FetchError("Unable to find revision %s in branch %s even from upstream" % (ud.revisions[name], ud.branches[name]))
378
379 def build_mirror_data(self, ud, d):
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500380 if ud.shallow and ud.write_shallow_tarballs:
381 if not os.path.exists(ud.fullshallow):
382 if os.path.islink(ud.fullshallow):
383 os.unlink(ud.fullshallow)
384 tempdir = tempfile.mkdtemp(dir=d.getVar('DL_DIR'))
385 shallowclone = os.path.join(tempdir, 'git')
386 try:
387 self.clone_shallow_local(ud, shallowclone, d)
388
389 logger.info("Creating tarball of git repository")
390 runfetchcmd("tar -czf %s ." % ud.fullshallow, d, workdir=shallowclone)
391 runfetchcmd("touch %s.done" % ud.fullshallow, d)
392 finally:
393 bb.utils.remove(tempdir, recurse=True)
394 elif ud.write_tarballs and not os.path.exists(ud.fullmirror):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500395 if os.path.islink(ud.fullmirror):
396 os.unlink(ud.fullmirror)
397
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500398 logger.info("Creating tarball of git repository")
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500399 runfetchcmd("tar -czf %s ." % ud.fullmirror, d, workdir=ud.clonedir)
400 runfetchcmd("touch %s.done" % ud.fullmirror, d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500401
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500402 def clone_shallow_local(self, ud, dest, d):
403 """Clone the repo and make it shallow.
404
405 The upstream url of the new clone isn't set at this time, as it'll be
406 set correctly when unpacked."""
407 runfetchcmd("%s clone %s %s %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, dest), d)
408
409 to_parse, shallow_branches = [], []
410 for name in ud.names:
411 revision = ud.revisions[name]
412 depth = ud.shallow_depths[name]
413 if depth:
414 to_parse.append('%s~%d^{}' % (revision, depth - 1))
415
416 # For nobranch, we need a ref, otherwise the commits will be
417 # removed, and for non-nobranch, we truncate the branch to our
418 # srcrev, to avoid keeping unnecessary history beyond that.
419 branch = ud.branches[name]
420 if ud.nobranch:
421 ref = "refs/shallow/%s" % name
422 elif ud.bareclone:
423 ref = "refs/heads/%s" % branch
424 else:
425 ref = "refs/remotes/origin/%s" % branch
426
427 shallow_branches.append(ref)
428 runfetchcmd("%s update-ref %s %s" % (ud.basecmd, ref, revision), d, workdir=dest)
429
430 # Map srcrev+depths to revisions
431 parsed_depths = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join(to_parse)), d, workdir=dest)
432
433 # Resolve specified revisions
434 parsed_revs = runfetchcmd("%s rev-parse %s" % (ud.basecmd, " ".join('"%s^{}"' % r for r in ud.shallow_revs)), d, workdir=dest)
435 shallow_revisions = parsed_depths.splitlines() + parsed_revs.splitlines()
436
437 # Apply extra ref wildcards
438 all_refs = runfetchcmd('%s for-each-ref "--format=%%(refname)"' % ud.basecmd,
439 d, workdir=dest).splitlines()
440 for r in ud.shallow_extra_refs:
441 if not ud.bareclone:
442 r = r.replace('refs/heads/', 'refs/remotes/origin/')
443
444 if '*' in r:
445 matches = filter(lambda a: fnmatch.fnmatchcase(a, r), all_refs)
446 shallow_branches.extend(matches)
447 else:
448 shallow_branches.append(r)
449
450 # Make the repository shallow
Brad Bishop316dfdd2018-06-25 12:45:53 -0400451 shallow_cmd = [self.make_shallow_path, '-s']
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500452 for b in shallow_branches:
453 shallow_cmd.append('-r')
454 shallow_cmd.append(b)
455 shallow_cmd.extend(shallow_revisions)
456 runfetchcmd(subprocess.list2cmdline(shallow_cmd), d, workdir=dest)
457
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500458 def unpack(self, ud, destdir, d):
459 """ unpack the downloaded src to destdir"""
460
461 subdir = ud.parm.get("subpath", "")
462 if subdir != "":
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500463 readpathspec = ":%s" % subdir
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500464 def_destsuffix = "%s/" % os.path.basename(subdir.rstrip('/'))
465 else:
466 readpathspec = ""
467 def_destsuffix = "git/"
468
469 destsuffix = ud.parm.get("destsuffix", def_destsuffix)
470 destdir = ud.destdir = os.path.join(destdir, destsuffix)
471 if os.path.exists(destdir):
472 bb.utils.prunedir(destdir)
473
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500474 if ud.shallow and (not os.path.exists(ud.clonedir) or self.need_update(ud, d)):
475 bb.utils.mkdirhier(destdir)
476 runfetchcmd("tar -xzf %s" % ud.fullshallow, d, workdir=destdir)
477 else:
478 runfetchcmd("%s clone %s %s/ %s" % (ud.basecmd, ud.cloneflags, ud.clonedir, destdir), d)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500479
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500480 repourl = self._get_repo_url(ud)
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600481 runfetchcmd("%s remote set-url origin %s" % (ud.basecmd, repourl), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500482 if not ud.nocheckout:
483 if subdir != "":
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600484 runfetchcmd("%s read-tree %s%s" % (ud.basecmd, ud.revisions[ud.names[0]], readpathspec), d,
485 workdir=destdir)
486 runfetchcmd("%s checkout-index -q -f -a" % ud.basecmd, d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500487 elif not ud.nobranch:
488 branchname = ud.branches[ud.names[0]]
489 runfetchcmd("%s checkout -B %s %s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600490 ud.revisions[ud.names[0]]), d, workdir=destdir)
Andre Rosa49271d42017-09-07 11:15:55 +0200491 runfetchcmd("%s branch %s --set-upstream-to origin/%s" % (ud.basecmd, branchname, \
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600492 branchname), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500493 else:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600494 runfetchcmd("%s checkout %s" % (ud.basecmd, ud.revisions[ud.names[0]]), d, workdir=destdir)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500495
496 return True
497
498 def clean(self, ud, d):
499 """ clean the git directory """
500
501 bb.utils.remove(ud.localpath, True)
502 bb.utils.remove(ud.fullmirror)
503 bb.utils.remove(ud.fullmirror + ".done")
504
505 def supports_srcrev(self):
506 return True
507
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600508 def _contains_ref(self, ud, d, name, wd):
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500509 cmd = ""
510 if ud.nobranch:
511 cmd = "%s log --pretty=oneline -n 1 %s -- 2> /dev/null | wc -l" % (
512 ud.basecmd, ud.revisions[name])
513 else:
514 cmd = "%s branch --contains %s --list %s 2> /dev/null | wc -l" % (
515 ud.basecmd, ud.revisions[name], ud.branches[name])
516 try:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600517 output = runfetchcmd(cmd, d, quiet=True, workdir=wd)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500518 except bb.fetch2.FetchError:
519 return False
520 if len(output.split()) > 1:
521 raise bb.fetch2.FetchError("The command '%s' gave output with more then 1 line unexpectedly, output: '%s'" % (cmd, output))
522 return output.split()[0] != "0"
523
524 def _get_repo_url(self, ud):
525 """
526 Return the repository URL
527 """
528 if ud.user:
529 username = ud.user + '@'
530 else:
531 username = ""
532 return "%s://%s%s%s" % (ud.proto, username, ud.host, ud.path)
533
534 def _revision_key(self, ud, d, name):
535 """
536 Return a unique key for the url
537 """
538 return "git:" + ud.host + ud.path.replace('/', '.') + ud.unresolvedrev[name]
539
540 def _lsremote(self, ud, d, search):
541 """
542 Run git ls-remote with the specified search string
543 """
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500544 # Prevent recursion e.g. in OE if SRCPV is in PV, PV is in WORKDIR,
545 # and WORKDIR is in PATH (as a result of RSS), our call to
546 # runfetchcmd() exports PATH so this function will get called again (!)
547 # In this scenario the return call of the function isn't actually
548 # important - WORKDIR isn't needed in PATH to call git ls-remote
549 # anyway.
550 if d.getVar('_BB_GIT_IN_LSREMOTE', False):
551 return ''
552 d.setVar('_BB_GIT_IN_LSREMOTE', '1')
553 try:
554 repourl = self._get_repo_url(ud)
555 cmd = "%s ls-remote %s %s" % \
556 (ud.basecmd, repourl, search)
557 if ud.proto.lower() != 'file':
558 bb.fetch2.check_network_access(d, cmd, repourl)
559 output = runfetchcmd(cmd, d, True)
560 if not output:
561 raise bb.fetch2.FetchError("The command %s gave empty output unexpectedly" % cmd, ud.url)
562 finally:
563 d.delVar('_BB_GIT_IN_LSREMOTE')
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500564 return output
565
566 def _latest_revision(self, ud, d, name):
567 """
568 Compute the HEAD revision for the url
569 """
570 output = self._lsremote(ud, d, "")
571 # Tags of the form ^{} may not work, need to fallback to other form
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600572 if ud.unresolvedrev[name][:5] == "refs/" or ud.usehead:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500573 head = ud.unresolvedrev[name]
574 tag = ud.unresolvedrev[name]
575 else:
576 head = "refs/heads/%s" % ud.unresolvedrev[name]
577 tag = "refs/tags/%s" % ud.unresolvedrev[name]
578 for s in [head, tag + "^{}", tag]:
Patrick Williamsc0f7c042017-02-23 20:41:17 -0600579 for l in output.strip().split('\n'):
580 sha1, ref = l.split()
581 if s == ref:
582 return sha1
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500583 raise bb.fetch2.FetchError("Unable to resolve '%s' in upstream git repository in git ls-remote output for %s" % \
584 (ud.unresolvedrev[name], ud.host+ud.path))
585
586 def latest_versionstring(self, ud, d):
587 """
588 Compute the latest release name like "x.y.x" in "x.y.x+gitHASH"
589 by searching through the tags output of ls-remote, comparing
590 versions and returning the highest match.
591 """
592 pupver = ('', '')
593
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500594 tagregex = re.compile(d.getVar('UPSTREAM_CHECK_GITTAGREGEX') or "(?P<pver>([0-9][\.|_]?)+)")
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500595 try:
596 output = self._lsremote(ud, d, "refs/tags/*")
Brad Bishop316dfdd2018-06-25 12:45:53 -0400597 except (bb.fetch2.FetchError, bb.fetch2.NetworkAccess) as e:
598 bb.note("Could not list remote: %s" % str(e))
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500599 return pupver
600
601 verstring = ""
602 revision = ""
603 for line in output.split("\n"):
604 if not line:
605 break
606
607 tag_head = line.split("/")[-1]
608 # Ignore non-released branches
609 m = re.search("(alpha|beta|rc|final)+", tag_head)
610 if m:
611 continue
612
613 # search for version in the line
614 tag = tagregex.search(tag_head)
615 if tag == None:
616 continue
617
618 tag = tag.group('pver')
619 tag = tag.replace("_", ".")
620
621 if verstring and bb.utils.vercmp(("0", tag, ""), ("0", verstring, "")) < 0:
622 continue
623
624 verstring = tag
625 revision = line.split()[0]
626 pupver = (verstring, revision)
627
628 return pupver
629
630 def _build_revision(self, ud, d, name):
631 return ud.revisions[name]
632
633 def gitpkgv_revision(self, ud, d, name):
634 """
635 Return a sortable revision number by counting commits in the history
636 Based on gitpkgv.bblass in meta-openembedded
637 """
638 rev = self._build_revision(ud, d, name)
639 localpath = ud.localpath
640 rev_file = os.path.join(localpath, "oe-gitpkgv_" + rev)
641 if not os.path.exists(localpath):
642 commits = None
643 else:
644 if not os.path.exists(rev_file) or not os.path.getsize(rev_file):
645 from pipes import quote
646 commits = bb.fetch2.runfetchcmd(
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500647 "git rev-list %s -- | wc -l" % quote(rev),
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500648 d, quiet=True).strip().lstrip('0')
649 if commits:
650 open(rev_file, "w").write("%d\n" % int(commits))
651 else:
652 commits = open(rev_file, "r").readline(128).strip()
653 if commits:
654 return False, "%s+%s" % (commits, rev[:7])
655 else:
656 return True, str(rev)
657
658 def checkstatus(self, fetch, ud, d):
659 try:
660 self._lsremote(ud, d, "")
661 return True
Brad Bishop6e60e8b2018-02-01 10:27:11 -0500662 except bb.fetch2.FetchError:
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500663 return False