net-fs/openafs-kernel: remove vulnerable versions
[gentoo.git] / eclass / git-r3.eclass
1 # Copyright 1999-2015 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 # @ECLASS: git-r3.eclass
6 # @MAINTAINER:
7 # Michał Górny <mgorny@gentoo.org>
8 # @BLURB: Eclass for fetching and unpacking git repositories.
9 # @DESCRIPTION:
10 # Third generation eclass for easing maintenance of live ebuilds using
11 # git as remote repository.
12
13 case "${EAPI:-0}" in
14         0|1|2|3|4|5)
15                 ;;
16         *)
17                 die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}"
18                 ;;
19 esac
20
21 if [[ ! ${_GIT_R3} ]]; then
22
23 inherit eutils
24
25 fi
26
27 EXPORT_FUNCTIONS src_unpack
28
29 if [[ ! ${_GIT_R3} ]]; then
30
31 if [[ ! ${_INHERITED_BY_GIT_2} ]]; then
32         DEPEND=">=dev-vcs/git-1.8.2.1"
33 fi
34
35 # @ECLASS-VARIABLE: EGIT_CLONE_TYPE
36 # @DESCRIPTION:
37 # Type of clone that should be used against the remote repository.
38 # This can be either of: 'mirror', 'single', 'shallow'.
39 #
40 # This is intended to be set by user in make.conf. Ebuilds are supposed
41 # to set EGIT_MIN_CLONE_TYPE if necessary instead.
42 #
43 # The 'mirror' type clones all remote branches and tags with complete
44 # history and all notes. EGIT_COMMIT can specify any commit hash.
45 # Upstream-removed branches and tags are purged from the local clone
46 # while fetching. This mode is suitable for cloning the local copy
47 # for development or hosting a local git mirror. However, clones
48 # of repositories with large diverged branches may quickly grow large.
49 #
50 # The 'single+tags' type clones the requested branch and all tags
51 # in the repository. All notes are fetched as well. EGIT_COMMIT
52 # can safely specify hashes throughout the current branch and all tags.
53 # No purging of old references is done (if you often switch branches,
54 # you may need to remove stale branches yourself). This mode is intended
55 # mostly for use with broken git servers such as Google Code that fail
56 # to fetch tags along with the branch in 'single' mode.
57 #
58 # The 'single' type clones only the requested branch or tag. Tags
59 # referencing commits throughout the branch history are fetched as well,
60 # and all notes. EGIT_COMMIT can safely specify only hashes
61 # in the current branch. No purging of old references is done (if you
62 # often switch branches, you may need to remove stale branches
63 # yourself). This mode is suitable for general use.
64 #
65 # The 'shallow' type clones only the newest commit on requested branch
66 # or tag. EGIT_COMMIT can only specify tags, and since the history is
67 # unavailable calls like 'git describe' will not reference prior tags.
68 # No purging of old references is done. This mode is intended mostly for
69 # embedded systems with limited disk space.
70 : ${EGIT_CLONE_TYPE:=single}
71
72 # @ECLASS-VARIABLE: EGIT_MIN_CLONE_TYPE
73 # @DESCRIPTION:
74 # 'Minimum' clone type supported by the ebuild. Takes same values
75 # as EGIT_CLONE_TYPE. When user sets a type that's 'lower' (that is,
76 # later on the list) than EGIT_MIN_CLONE_TYPE, the eclass uses
77 # EGIT_MIN_CLONE_TYPE instead.
78 #
79 # This variable is intended to be used by ebuilds only. Users are
80 # supposed to set EGIT_CLONE_TYPE instead.
81 #
82 # A common case is to use 'single' whenever the build system requires
83 # access to full branch history, or 'single+tags' when Google Code
84 # or a similar remote is used that does not support shallow clones
85 # and fetching tags along with commits. Please use sparingly, and to fix
86 # fatal errors rather than 'non-pretty versions'.
87 : ${EGIT_MIN_CLONE_TYPE:=shallow}
88
89 # @ECLASS-VARIABLE: EGIT3_STORE_DIR
90 # @DESCRIPTION:
91 # Storage directory for git sources.
92 #
93 # This is intended to be set by user in make.conf. Ebuilds must not set
94 # it.
95 #
96 # EGIT3_STORE_DIR=${DISTDIR}/git3-src
97
98 # @ECLASS-VARIABLE: EGIT_MIRROR_URI
99 # @DEFAULT_UNSET
100 # @DESCRIPTION:
101 # 'Top' URI to a local git mirror. If specified, the eclass will try
102 # to fetch from the local mirror instead of using the remote repository.
103 #
104 # The mirror needs to follow EGIT3_STORE_DIR structure. The directory
105 # created by eclass can be used for that purpose.
106 #
107 # Example:
108 # @CODE
109 # EGIT_MIRROR_URI="git://mirror.lan/"
110 # @CODE
111
112 # @ECLASS-VARIABLE: EGIT_REPO_URI
113 # @REQUIRED
114 # @DESCRIPTION:
115 # URIs to the repository, e.g. git://foo, https://foo. If multiple URIs
116 # are provided, the eclass will consider them as fallback URIs to try
117 # if the first URI does not work. For supported URI syntaxes, read up
118 # the manpage for git-clone(1).
119 #
120 # It can be overriden via env using ${PN}_LIVE_REPO variable.
121 #
122 # Can be a whitespace-separated list or an array.
123 #
124 # Example:
125 # @CODE
126 # EGIT_REPO_URI="git://a/b.git https://c/d.git"
127 # @CODE
128
129 # @ECLASS-VARIABLE: EVCS_OFFLINE
130 # @DEFAULT_UNSET
131 # @DESCRIPTION:
132 # If non-empty, this variable prevents any online operations.
133
134 # @ECLASS-VARIABLE: EVCS_UMASK
135 # @DEFAULT_UNSET
136 # @DESCRIPTION:
137 # Set this variable to a custom umask. This is intended to be set by
138 # users. By setting this to something like 002, it can make life easier
139 # for people who do development as non-root (but are in the portage
140 # group), and then switch over to building with FEATURES=userpriv.
141 # Or vice-versa. Shouldn't be a security issue here as anyone who has
142 # portage group write access already can screw the system over in more
143 # creative ways.
144
145 # @ECLASS-VARIABLE: EGIT_BRANCH
146 # @DEFAULT_UNSET
147 # @DESCRIPTION:
148 # The branch name to check out. If unset, the upstream default (HEAD)
149 # will be used.
150 #
151 # It can be overriden via env using ${PN}_LIVE_BRANCH variable.
152
153 # @ECLASS-VARIABLE: EGIT_COMMIT
154 # @DEFAULT_UNSET
155 # @DESCRIPTION:
156 # The tag name or commit identifier to check out. If unset, newest
157 # commit from the branch will be used. If set, EGIT_BRANCH will
158 # be ignored.
159 #
160 # It can be overriden via env using ${PN}_LIVE_COMMIT variable.
161
162 # @ECLASS-VARIABLE: EGIT_CHECKOUT_DIR
163 # @DESCRIPTION:
164 # The directory to check the git sources out to.
165 #
166 # EGIT_CHECKOUT_DIR=${WORKDIR}/${P}
167
168 # @FUNCTION: _git-r3_env_setup
169 # @INTERNAL
170 # @DESCRIPTION:
171 # Set the eclass variables as necessary for operation. This can involve
172 # setting EGIT_* to defaults or ${PN}_LIVE_* variables.
173 _git-r3_env_setup() {
174         debug-print-function ${FUNCNAME} "$@"
175
176         # check the clone type
177         case "${EGIT_CLONE_TYPE}" in
178                 mirror|single+tags|single|shallow)
179                         ;;
180                 *)
181                         die "Invalid EGIT_CLONE_TYPE=${EGIT_CLONE_TYPE}"
182         esac
183         case "${EGIT_MIN_CLONE_TYPE}" in
184                 shallow)
185                         ;;
186                 single)
187                         if [[ ${EGIT_CLONE_TYPE} == shallow ]]; then
188                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle\e[22m' mode, adjusting"
189                                 EGIT_CLONE_TYPE=single
190                         fi
191                         ;;
192                 single+tags)
193                         if [[ ${EGIT_CLONE_TYPE} == shallow || ${EGIT_CLONE_TYPE} == single ]]; then
194                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle+tags\e[22m' mode, adjusting"
195                                 EGIT_CLONE_TYPE=single+tags
196                         fi
197                         ;;
198                 mirror)
199                         if [[ ${EGIT_CLONE_TYPE} != mirror ]]; then
200                                 einfo "git-r3: ebuild needs to be cloned in '\e[1mmirror\e[22m' mode, adjusting"
201                                 EGIT_CLONE_TYPE=mirror
202                         fi
203                         ;;
204                 *)
205                         die "Invalid EGIT_MIN_CLONE_TYPE=${EGIT_MIN_CLONE_TYPE}"
206         esac
207
208         local esc_pn livevar
209         esc_pn=${PN//[-+]/_}
210
211         livevar=${esc_pn}_LIVE_REPO
212         EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}}
213         [[ ${!livevar} ]] \
214                 && ewarn "Using ${livevar}, no support will be provided"
215
216         livevar=${esc_pn}_LIVE_BRANCH
217         EGIT_BRANCH=${!livevar:-${EGIT_BRANCH}}
218         [[ ${!livevar} ]] \
219                 && ewarn "Using ${livevar}, no support will be provided"
220
221         livevar=${esc_pn}_LIVE_COMMIT
222         EGIT_COMMIT=${!livevar:-${EGIT_COMMIT}}
223         [[ ${!livevar} ]] \
224                 && ewarn "Using ${livevar}, no support will be provided"
225
226         # Migration helpers. Remove them when git-2 is removed.
227
228         if [[ ${EGIT_SOURCEDIR} ]]; then
229                 eerror "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR. While updating"
230                 eerror "your ebuild, please check whether the variable is necessary at all"
231                 eerror "since the default has been changed from \${S} to \${WORKDIR}/\${P}."
232                 eerror "Therefore, proper setting of S may be sufficient."
233                 die "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR."
234         fi
235
236         if [[ ${EGIT_MASTER} ]]; then
237                 eerror "EGIT_MASTER has been removed. Instead, the upstream default (HEAD)"
238                 eerror "is used by the eclass. Please remove the assignment or use EGIT_BRANCH"
239                 eerror "as necessary."
240                 die "EGIT_MASTER has been removed."
241         fi
242
243         if [[ ${EGIT_HAS_SUBMODULES} ]]; then
244                 eerror "EGIT_HAS_SUBMODULES has been removed. The eclass no longer needs"
245                 eerror "to switch the clone type in order to support submodules and therefore"
246                 eerror "submodules are detected and fetched automatically."
247                 die "EGIT_HAS_SUBMODULES is no longer necessary."
248         fi
249
250         if [[ ${EGIT_PROJECT} ]]; then
251                 eerror "EGIT_PROJECT has been removed. Instead, the eclass determines"
252                 eerror "the local clone path using path in canonical EGIT_REPO_URI."
253                 eerror "If the current algorithm causes issues for you, please report a bug."
254                 die "EGIT_PROJECT is no longer necessary."
255         fi
256
257         if [[ ${EGIT_BOOTSTRAP} ]]; then
258                 eerror "EGIT_BOOTSTRAP has been removed. Please create proper src_prepare()"
259                 eerror "instead."
260                 die "EGIT_BOOTSTRAP has been removed."
261         fi
262
263         if [[ ${EGIT_NOUNPACK} ]]; then
264                 eerror "EGIT_NOUNPACK has been removed. The eclass no longer calls default"
265                 eerror "unpack function. If necessary, please declare proper src_unpack()."
266                 die "EGIT_NOUNPACK has been removed."
267         fi
268 }
269
270 # @FUNCTION: _git-r3_set_gitdir
271 # @USAGE: <repo-uri>
272 # @INTERNAL
273 # @DESCRIPTION:
274 # Obtain the local repository path and set it as GIT_DIR. Creates
275 # a new repository if necessary.
276 #
277 # <repo-uri> may be used to compose the path. It should therefore be
278 # a canonical URI to the repository.
279 _git-r3_set_gitdir() {
280         debug-print-function ${FUNCNAME} "$@"
281
282         local repo_name=${1#*://*/}
283
284         # strip the trailing slash
285         repo_name=${repo_name%/}
286
287         # strip common prefixes to make paths more likely to match
288         # e.g. git://X/Y.git vs https://X/git/Y.git
289         # (but just one of the prefixes)
290         case "${repo_name}" in
291                 # gnome.org... who else?
292                 browse/*) repo_name=${repo_name#browse/};;
293                 # cgit can proxy requests to git
294                 cgit/*) repo_name=${repo_name#cgit/};;
295                 # pretty common
296                 git/*) repo_name=${repo_name#git/};;
297                 # gentoo.org
298                 gitroot/*) repo_name=${repo_name#gitroot/};;
299                 # google code, sourceforge
300                 p/*) repo_name=${repo_name#p/};;
301                 # kernel.org
302                 pub/scm/*) repo_name=${repo_name#pub/scm/};;
303         esac
304         # ensure a .git suffix, same reason
305         repo_name=${repo_name%.git}.git
306         # now replace all the slashes
307         repo_name=${repo_name//\//_}
308
309         local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}
310         : ${EGIT3_STORE_DIR:=${distdir}/git3-src}
311
312         GIT_DIR=${EGIT3_STORE_DIR}/${repo_name}
313
314         if [[ ! -d ${EGIT3_STORE_DIR} ]]; then
315                 (
316                         addwrite /
317                         mkdir -p "${EGIT3_STORE_DIR}" || die
318                 ) || die "Unable to create ${EGIT3_STORE_DIR}"
319         fi
320
321         addwrite "${EGIT3_STORE_DIR}"
322         if [[ ! -d ${GIT_DIR} ]]; then
323                 local saved_umask
324                 if [[ ${EVCS_UMASK} ]]; then
325                         saved_umask=$(umask)
326                         umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
327                 fi
328                 mkdir "${GIT_DIR}" || die
329                 git init --bare || die
330                 if [[ ${saved_umask} ]]; then
331                         umask "${saved_umask}" || die
332                 fi
333         fi
334 }
335
336 # @FUNCTION: _git-r3_set_submodules
337 # @USAGE: <file-contents>
338 # @INTERNAL
339 # @DESCRIPTION:
340 # Parse .gitmodules contents passed as <file-contents>
341 # as in "$(cat .gitmodules)"). Composes a 'submodules' array that
342 # contains in order (name, URL, path) for each submodule.
343 _git-r3_set_submodules() {
344         debug-print-function ${FUNCNAME} "$@"
345
346         local data=${1}
347
348         # ( name url path ... )
349         submodules=()
350
351         local l
352         while read l; do
353                 # submodule.<path>.path=<path>
354                 # submodule.<path>.url=<url>
355                 [[ ${l} == submodule.*.url=* ]] || continue
356
357                 l=${l#submodule.}
358                 local subname=${l%%.url=*}
359
360                 # skip modules that have 'update = none', bug #487262.
361                 local upd=$(echo "${data}" | git config -f /dev/fd/0 \
362                         submodule."${subname}".update)
363                 [[ ${upd} == none ]] && continue
364
365                 submodules+=(
366                         "${subname}"
367                         "$(echo "${data}" | git config -f /dev/fd/0 \
368                                 submodule."${subname}".url || die)"
369                         "$(echo "${data}" | git config -f /dev/fd/0 \
370                                 submodule."${subname}".path || die)"
371                 )
372         done < <(echo "${data}" | git config -f /dev/fd/0 -l || die)
373 }
374
375 # @FUNCTION: _git-r3_set_subrepos
376 # @USAGE: <submodule-uri> <parent-repo-uri>...
377 # @INTERNAL
378 # @DESCRIPTION:
379 # Create 'subrepos' array containing absolute (canonical) submodule URIs
380 # for the given <submodule-uri>. If the URI is relative, URIs will be
381 # constructed using all <parent-repo-uri>s. Otherwise, this single URI
382 # will be placed in the array.
383 _git-r3_set_subrepos() {
384         debug-print-function ${FUNCNAME} "$@"
385
386         local suburl=${1}
387         subrepos=( "${@:2}" )
388
389         if [[ ${suburl} == ./* || ${suburl} == ../* ]]; then
390                 # drop all possible trailing slashes for consistency
391                 subrepos=( "${subrepos[@]%%/}" )
392
393                 while true; do
394                         if [[ ${suburl} == ./* ]]; then
395                                 suburl=${suburl:2}
396                         elif [[ ${suburl} == ../* ]]; then
397                                 suburl=${suburl:3}
398
399                                 # XXX: correctness checking
400
401                                 # drop the last path component
402                                 subrepos=( "${subrepos[@]%/*}" )
403                                 # and then the trailing slashes, again
404                                 subrepos=( "${subrepos[@]%%/}" )
405                         else
406                                 break
407                         fi
408                 done
409
410                 # append the preprocessed path to the preprocessed URIs
411                 subrepos=( "${subrepos[@]/%//${suburl}}")
412         else
413                 subrepos=( "${suburl}" )
414         fi
415 }
416
417
418 # @FUNCTION: _git-r3_is_local_repo
419 # @USAGE: <repo-uri>
420 # @INTERNAL
421 # @DESCRIPTION:
422 # Determine whether the given URI specifies a local (on-disk)
423 # repository.
424 _git-r3_is_local_repo() {
425         debug-print-function ${FUNCNAME} "$@"
426
427         local uri=${1}
428
429         [[ ${uri} == file://* || ${uri} == /* ]]
430 }
431
432 # @FUNCTION: _git-r3_find_head
433 # @USAGE: <head-ref>
434 # @INTERNAL
435 # @DESCRIPTION:
436 # Given a ref to which remote HEAD was fetched, try to find
437 # a branch matching the commit. Expects 'git show-ref'
438 # or 'git ls-remote' output on stdin.
439 _git-r3_find_head() {
440         debug-print-function ${FUNCNAME} "$@"
441
442         local head_ref=${1}
443         local head_hash=$(git rev-parse --verify "${1}" || die)
444         local matching_ref
445
446         # TODO: some transports support peeking at symbolic remote refs
447         # find a way to use that rather than guessing
448
449         # (based on guess_remote_head() in git-1.9.0/remote.c)
450         local h ref
451         while read h ref; do
452                 # look for matching head
453                 if [[ ${h} == ${head_hash} ]]; then
454                         # either take the first matching ref, or master if it is there
455                         if [[ ! ${matching_ref} || ${ref} == refs/heads/master ]]; then
456                                 matching_ref=${ref}
457                         fi
458                 fi
459         done
460
461         if [[ ! ${matching_ref} ]]; then
462                 die "Unable to find a matching branch for remote HEAD (${head_hash})"
463         fi
464
465         echo "${matching_ref}"
466 }
467
468 # @FUNCTION: git-r3_fetch
469 # @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]]
470 # @DESCRIPTION:
471 # Fetch new commits to the local clone of repository.
472 #
473 # <repo-uri> specifies the repository URIs to fetch from, as a space-
474 # -separated list. The first URI will be used as repository group
475 # identifier and therefore must be used consistently. When not
476 # specified, defaults to ${EGIT_REPO_URI}.
477 #
478 # <remote-ref> specifies the remote ref or commit id to fetch.
479 # It is preferred to use 'refs/heads/<branch-name>' for branches
480 # and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD'
481 # for upstream default branch and hexadecimal commit SHA1. Defaults
482 # to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that
483 # is set to a non-null value.
484 #
485 # <local-id> specifies the local branch identifier that will be used to
486 # locally store the fetch result. It should be unique to multiple
487 # fetches within the repository that can be performed at the same time
488 # (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT%/*}.
489 # This default should be fine unless you are fetching multiple trees
490 # from the same repository in the same ebuild.
491 #
492 # The fetch operation will affect the EGIT_STORE only. It will not touch
493 # the working copy, nor export any environment variables.
494 # If the repository contains submodules, they will be fetched
495 # recursively.
496 git-r3_fetch() {
497         debug-print-function ${FUNCNAME} "$@"
498
499         [[ ${EVCS_OFFLINE} ]] && return
500
501         local repos
502         if [[ ${1} ]]; then
503                 repos=( ${1} )
504         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
505                 repos=( "${EGIT_REPO_URI[@]}" )
506         else
507                 repos=( ${EGIT_REPO_URI} )
508         fi
509
510         local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
511         local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
512         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
513         local local_ref=refs/git-r3/${local_id}/__main__
514
515         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
516
517         local -x GIT_DIR
518         _git-r3_set_gitdir "${repos[0]}"
519
520         # prepend the local mirror if applicable
521         if [[ ${EGIT_MIRROR_URI} ]]; then
522                 repos=(
523                         "${EGIT_MIRROR_URI%/}/${GIT_DIR##*/}"
524                         "${repos[@]}"
525                 )
526         fi
527
528         # try to fetch from the remote
529         local r success saved_umask
530         if [[ ${EVCS_UMASK} ]]; then
531                 saved_umask=$(umask)
532                 umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
533         fi
534         for r in "${repos[@]}"; do
535                 einfo "Fetching \e[1m${r}\e[22m ..."
536
537                 local fetch_command=( git fetch "${r}" )
538                 local clone_type=${EGIT_CLONE_TYPE}
539
540                 if [[ ${r} == https://* ]] && ! ROOT=/ has_version 'dev-vcs/git[curl]'; then
541                         eerror "git-r3: fetching from https:// requested. In order to support https,"
542                         eerror "dev-vcs/git needs to be built with USE=curl. Example solution:"
543                         eerror
544                         eerror "        echo dev-vcs/git curl >> /etc/portage/package.use"
545                         eerror "        emerge -1v dev-vcs/git"
546                         die "dev-vcs/git built with USE=curl required."
547                 fi
548
549                 if [[ ${r} == https://code.google.com/* ]]; then
550                         # Google Code has special magic on top of git that:
551                         # 1) can't handle shallow clones at all,
552                         # 2) fetches duplicately when tags are pulled in with branch
553                         # so automatically switch to single+tags mode.
554                         if [[ ${clone_type} == shallow ]]; then
555                                 einfo "  Google Code does not support shallow clones"
556                                 einfo "  using \e[1mEGIT_CLONE_TYPE=single+tags\e[22m"
557                                 clone_type=single+tags
558                         elif [[ ${clone_type} == single ]]; then
559                                 einfo "  git-r3: Google Code does not send tags properly in 'single' mode"
560                                 einfo "  using \e[1mEGIT_CLONE_TYPE=single+tags\e[22m"
561                                 clone_type=single+tags
562                         fi
563                 fi
564
565                 if [[ ${clone_type} == mirror ]]; then
566                         fetch_command+=(
567                                 --prune
568                                 # mirror the remote branches as local branches
569                                 "+refs/heads/*:refs/heads/*"
570                                 # pull tags explicitly in order to prune them properly
571                                 "+refs/tags/*:refs/tags/*"
572                                 # notes in case something needs them
573                                 "+refs/notes/*:refs/notes/*"
574                                 # and HEAD in case we need the default branch
575                                 # (we keep it in refs/git-r3 since otherwise --prune interferes)
576                                 "+HEAD:refs/git-r3/HEAD"
577                         )
578                 else # single or shallow
579                         local fetch_l fetch_r
580
581                         if [[ ${remote_ref} == HEAD ]]; then
582                                 # HEAD
583                                 fetch_l=HEAD
584                         elif [[ ${remote_ref} == refs/heads/* ]]; then
585                                 # regular branch
586                                 fetch_l=${remote_ref}
587                         else
588                                 # tag or commit...
589                                 # let ls-remote figure it out
590                                 local tagref=$(git ls-remote "${r}" "refs/tags/${remote_ref}")
591
592                                 # if it was a tag, ls-remote obtained a hash
593                                 if [[ ${tagref} ]]; then
594                                         # tag
595                                         fetch_l=refs/tags/${remote_ref}
596                                 else
597                                         # commit
598                                         # so we need to fetch the branch
599                                         if [[ ${branch} ]]; then
600                                                 fetch_l=${branch}
601                                         else
602                                                 fetch_l=HEAD
603                                         fi
604
605                                         # fetching by commit in shallow mode? can't do.
606                                         if [[ ${clone_type} == shallow ]]; then
607                                                 clone_type=single
608                                         fi
609                                 fi
610                         fi
611
612                         if [[ ${fetch_l} == HEAD ]]; then
613                                 fetch_r=refs/git-r3/HEAD
614                         else
615                                 fetch_r=${fetch_l}
616                         fi
617
618                         fetch_command+=(
619                                 "+${fetch_l}:${fetch_r}"
620                         )
621
622                         if [[ ${clone_type} == single+tags ]]; then
623                                 fetch_command+=(
624                                         # pull tags explicitly as requested
625                                         "+refs/tags/*:refs/tags/*"
626                                 )
627                         fi
628                 fi
629
630                 if [[ ${clone_type} == shallow ]]; then
631                         if _git-r3_is_local_repo; then
632                                 # '--depth 1' causes sandbox violations with local repos
633                                 # bug #491260
634                                 clone_type=single
635                         elif [[ ! $(git rev-parse --quiet --verify "${fetch_r}") ]]
636                         then
637                                 # use '--depth 1' when fetching a new branch
638                                 fetch_command+=( --depth 1 )
639                         fi
640                 else # non-shallow mode
641                         if [[ -f ${GIT_DIR}/shallow ]]; then
642                                 fetch_command+=( --unshallow )
643                         fi
644                 fi
645
646                 set -- "${fetch_command[@]}"
647                 echo "${@}" >&2
648                 if "${@}"; then
649                         if [[ ${clone_type} == mirror ]]; then
650                                 # find remote HEAD and update our HEAD properly
651                                 git symbolic-ref HEAD \
652                                         "$(_git-r3_find_head refs/git-r3/HEAD \
653                                                 < <(git show-ref --heads || die))" \
654                                                 || die "Unable to update HEAD"
655                         else # single or shallow
656                                 if [[ ${fetch_l} == HEAD ]]; then
657                                         # find out what branch we fetched as HEAD
658                                         local head_branch=$(_git-r3_find_head \
659                                                 refs/git-r3/HEAD \
660                                                 < <(git ls-remote --heads "${r}" || die))
661
662                                         # and move it to its regular place
663                                         git update-ref --no-deref "${head_branch}" \
664                                                 refs/git-r3/HEAD \
665                                                 || die "Unable to sync HEAD branch ${head_branch}"
666                                         git symbolic-ref HEAD "${head_branch}" \
667                                                 || die "Unable to update HEAD"
668                                 fi
669                         fi
670
671                         # now let's see what the user wants from us
672                         local full_remote_ref=$(
673                                 git rev-parse --verify --symbolic-full-name "${remote_ref}"
674                         )
675
676                         if [[ ${full_remote_ref} ]]; then
677                                 # when we are given a ref, create a symbolic ref
678                                 # so that we preserve the actual argument
679                                 set -- git symbolic-ref "${local_ref}" "${full_remote_ref}"
680                         else
681                                 # otherwise, we were likely given a commit id
682                                 set -- git update-ref --no-deref "${local_ref}" "${remote_ref}"
683                         fi
684
685                         echo "${@}" >&2
686                         if ! "${@}"; then
687                                 die "Referencing ${remote_ref} failed (wrong ref?)."
688                         fi
689
690                         success=1
691                         break
692                 fi
693         done
694         if [[ ${saved_umask} ]]; then
695                 umask "${saved_umask}" || die
696         fi
697         [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI"
698
699         # submodules can reference commits in any branch
700         # always use the 'clone' mode to accomodate that, bug #503332
701         local EGIT_CLONE_TYPE=mirror
702
703         # recursively fetch submodules
704         if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then
705                 local submodules
706                 _git-r3_set_submodules \
707                         "$(git cat-file -p "${local_ref}":.gitmodules || die)"
708
709                 while [[ ${submodules[@]} ]]; do
710                         local subname=${submodules[0]}
711                         local url=${submodules[1]}
712                         local path=${submodules[2]}
713
714                         # use only submodules for which path does exist
715                         # (this is in par with 'git submodule'), bug #551100
716                         # note: git cat-file does not work for submodules
717                         if [[ $(git ls-tree -d "${local_ref}" "${path}") ]]
718                         then
719                                 local commit=$(git rev-parse "${local_ref}:${path}" || die)
720
721                                 if [[ ! ${commit} ]]; then
722                                         die "Unable to get commit id for submodule ${subname}"
723                                 fi
724
725                                 local subrepos
726                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
727
728                                 git-r3_fetch "${subrepos[*]}" "${commit}" "${local_id}/${subname}"
729                         fi
730
731                         submodules=( "${submodules[@]:3}" ) # shift
732                 done
733         fi
734 }
735
736 # @FUNCTION: git-r3_checkout
737 # @USAGE: [<repo-uri> [<checkout-path> [<local-id>]]]
738 # @DESCRIPTION:
739 # Check the previously fetched tree to the working copy.
740 #
741 # <repo-uri> specifies the repository URIs, as a space-separated list.
742 # The first URI will be used as repository group identifier
743 # and therefore must be used consistently with git-r3_fetch.
744 # The remaining URIs are not used and therefore may be omitted.
745 # When not specified, defaults to ${EGIT_REPO_URI}.
746 #
747 # <checkout-path> specifies the path to place the checkout. It defaults
748 # to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}.
749 #
750 # <local-id> needs to specify the local identifier that was used
751 # for respective git-r3_fetch.
752 #
753 # The checkout operation will write to the working copy, and export
754 # the repository state into the environment. If the repository contains
755 # submodules, they will be checked out recursively.
756 git-r3_checkout() {
757         debug-print-function ${FUNCNAME} "$@"
758
759         local repos
760         if [[ ${1} ]]; then
761                 repos=( ${1} )
762         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
763                 repos=( "${EGIT_REPO_URI[@]}" )
764         else
765                 repos=( ${EGIT_REPO_URI} )
766         fi
767
768         local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}}
769         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
770
771         local -x GIT_DIR
772         _git-r3_set_gitdir "${repos[0]}"
773
774         einfo "Checking out \e[1m${repos[0]}\e[22m to \e[1m${out_dir}\e[22m ..."
775
776         if ! git cat-file -e refs/git-r3/"${local_id}"/__main__; then
777                 if [[ ${EVCS_OFFLINE} ]]; then
778                         die "No local clone of ${repos[0]}. Unable to work with EVCS_OFFLINE."
779                 else
780                         die "Logic error: no local clone of ${repos[0]}. git-r3_fetch not used?"
781                 fi
782         fi
783         local remote_ref=$(
784                 git symbolic-ref --quiet refs/git-r3/"${local_id}"/__main__
785         )
786         local new_commit_id=$(
787                 git rev-parse --verify refs/git-r3/"${local_id}"/__main__
788         )
789
790         git-r3_sub_checkout() {
791                 local orig_repo=${GIT_DIR}
792                 local -x GIT_DIR=${out_dir}/.git
793                 local -x GIT_WORK_TREE=${out_dir}
794
795                 mkdir -p "${out_dir}" || die
796
797                 # use git init+fetch instead of clone since the latter doesn't like
798                 # non-empty directories.
799
800                 git init --quiet || die
801                 # setup 'alternates' to avoid copying objects
802                 echo "${orig_repo}/objects" > "${GIT_DIR}"/objects/info/alternates || die
803                 # now copy the refs
804                 # [htn]* safely catches heads, tags, notes without complaining
805                 # on non-existing ones, and omits internal 'git-r3' ref
806                 cp -R "${orig_repo}"/refs/[htn]* "${GIT_DIR}"/refs/ || die
807
808                 # (no need to copy HEAD, we will set it via checkout)
809
810                 if [[ -f ${orig_repo}/shallow ]]; then
811                         cp "${orig_repo}"/shallow "${GIT_DIR}"/ || die
812                 fi
813
814                 set -- git checkout --quiet
815                 if [[ ${remote_ref} ]]; then
816                         set -- "${@}" "${remote_ref#refs/heads/}"
817                 else
818                         set -- "${@}" "${new_commit_id}"
819                 fi
820                 echo "${@}" >&2
821                 "${@}" || die "git checkout ${remote_ref:-${new_commit_id}} failed"
822         }
823         git-r3_sub_checkout
824
825         local old_commit_id=$(
826                 git rev-parse --quiet --verify refs/git-r3/"${local_id}"/__old__
827         )
828         if [[ ! ${old_commit_id} ]]; then
829                 echo "GIT NEW branch -->"
830                 echo "   repository:               ${repos[0]}"
831                 echo "   at the commit:            ${new_commit_id}"
832         else
833                 # diff against previous revision
834                 echo "GIT update -->"
835                 echo "   repository:               ${repos[0]}"
836                 # write out message based on the revisions
837                 if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then
838                         echo "   updating from commit:     ${old_commit_id}"
839                         echo "   to commit:                ${new_commit_id}"
840
841                         git --no-pager diff --stat \
842                                 ${old_commit_id}..${new_commit_id}
843                 else
844                         echo "   at the commit:            ${new_commit_id}"
845                 fi
846         fi
847         git update-ref --no-deref refs/git-r3/"${local_id}"/{__old__,__main__} || die
848
849         # recursively checkout submodules
850         if [[ -f ${out_dir}/.gitmodules ]]; then
851                 local submodules
852                 _git-r3_set_submodules \
853                         "$(<"${out_dir}"/.gitmodules)"
854
855                 while [[ ${submodules[@]} ]]; do
856                         local subname=${submodules[0]}
857                         local url=${submodules[1]}
858                         local path=${submodules[2]}
859
860                         # use only submodules for which path does exist
861                         # (this is in par with 'git submodule'), bug #551100
862                         if [[ -d ${out_dir}/${path} ]]; then
863                                 local subrepos
864                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
865
866                                 git-r3_checkout "${subrepos[*]}" "${out_dir}/${path}" \
867                                         "${local_id}/${subname}"
868                         fi
869
870                         submodules=( "${submodules[@]:3}" ) # shift
871                 done
872         fi
873
874         # keep this *after* submodules
875         export EGIT_DIR=${GIT_DIR}
876         export EGIT_VERSION=${new_commit_id}
877 }
878
879 # @FUNCTION: git-r3_peek_remote_ref
880 # @USAGE: [<repo-uri> [<remote-ref>]]
881 # @DESCRIPTION:
882 # Peek the reference in the remote repository and print the matching
883 # (newest) commit SHA1.
884 #
885 # <repo-uri> specifies the repository URIs to fetch from, as a space-
886 # -separated list. When not specified, defaults to ${EGIT_REPO_URI}.
887 #
888 # <remote-ref> specifies the remote ref to peek.  It is preferred to use
889 # 'refs/heads/<branch-name>' for branches and 'refs/tags/<tag-name>'
890 # for tags. Alternatively, 'HEAD' may be used for upstream default
891 # branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal
892 # 'HEAD' that is set to a non-null value.
893 #
894 # The operation will be done purely on the remote, without using local
895 # storage. If commit SHA1 is provided as <remote-ref>, the function will
896 # fail due to limitations of git protocol.
897 #
898 # On success, the function returns 0 and writes hexadecimal commit SHA1
899 # to stdout. On failure, the function returns 1.
900 git-r3_peek_remote_ref() {
901         debug-print-function ${FUNCNAME} "$@"
902
903         local repos
904         if [[ ${1} ]]; then
905                 repos=( ${1} )
906         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
907                 repos=( "${EGIT_REPO_URI[@]}" )
908         else
909                 repos=( ${EGIT_REPO_URI} )
910         fi
911
912         local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
913         local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
914
915         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
916
917         local r success
918         for r in "${repos[@]}"; do
919                 einfo "Peeking \e[1m${remote_ref}\e[22m on \e[1m${r}\e[22m ..." >&2
920
921                 local is_branch lookup_ref
922                 if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]]
923                 then
924                         is_branch=1
925                         lookup_ref=${remote_ref}
926                 else
927                         # ls-remote by commit is going to fail anyway,
928                         # so we may as well pass refs/tags/ABCDEF...
929                         lookup_ref=refs/tags/${remote_ref}
930                 fi
931
932                 # split on whitespace
933                 local ref=(
934                         $(git ls-remote "${r}" "${lookup_ref}")
935                 )
936
937                 if [[ ${ref[0]} ]]; then
938                         echo "${ref[0]}"
939                         return 0
940                 fi
941         done
942
943         return 1
944 }
945
946 git-r3_src_fetch() {
947         debug-print-function ${FUNCNAME} "$@"
948
949         if [[ ! ${EGIT3_STORE_DIR} && ${EGIT_STORE_DIR} ]]; then
950                 ewarn "You have set EGIT_STORE_DIR but not EGIT3_STORE_DIR. Please consider"
951                 ewarn "setting EGIT3_STORE_DIR for git-r3.eclass. It is recommended to use"
952                 ewarn "a different directory than EGIT_STORE_DIR to ease removing old clones"
953                 ewarn "when git-2 eclass becomes deprecated."
954         fi
955
956         _git-r3_env_setup
957         git-r3_fetch
958 }
959
960 git-r3_src_unpack() {
961         debug-print-function ${FUNCNAME} "$@"
962
963         _git-r3_env_setup
964         git-r3_src_fetch
965         git-r3_checkout
966 }
967
968 # https://bugs.gentoo.org/show_bug.cgi?id=482666
969 git-r3_pkg_needrebuild() {
970         debug-print-function ${FUNCNAME} "$@"
971
972         local new_commit_id=$(git-r3_peek_remote_ref)
973         [[ ${new_commit_id} && ${EGIT_VERSION} ]] || die "Lookup failed"
974
975         if [[ ${EGIT_VERSION} != ${new_commit_id} ]]; then
976                 einfo "Update from \e[1m${EGIT_VERSION}\e[22m to \e[1m${new_commit_id}\e[22m"
977         else
978                 einfo "Local and remote at \e[1m${EGIT_VERSION}\e[22m"
979         fi
980
981         [[ ${EGIT_VERSION} != ${new_commit_id} ]]
982 }
983
984 # 'export' locally until this gets into EAPI
985 pkg_needrebuild() { git-r3_pkg_needrebuild; }
986
987 _GIT_R3=1
988 fi