git-r3.eclass: Support EGIT_SUBMODULES to filter used submodules, #497164
[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|6)
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 # @ECLASS-VARIABLE: EGIT_SUBMODULES
169 # @DEFAULT_UNSET
170 # @DESCRIPTION:
171 # An array of inclusive and exclusive wildcards on submodule names,
172 # stating which submodules are fetched and checked out. Exclusions
173 # start with '-', and exclude previously matched submodules.
174 #
175 # If unset, all submodules are enabled. Empty list disables all
176 # submodules. In order to use an exclude-only list, start the array
177 # with '*'.
178 #
179 # Remember that wildcards need to be quoted in order to prevent filename
180 # expansion.
181 #
182 # Examples:
183 # @CODE
184 # # Disable all submodules
185 # EGIT_SUBMODULES=()
186 #
187 # # Include only foo and bar
188 # EGIT_SUBMODULES=( foo bar )
189 #
190 # # Use all submodules except for test-* but include test-lib
191 # EGIT_SUBMODULES=( '*' '-test-*' test-lib )
192 # @CODE
193
194 # @FUNCTION: _git-r3_env_setup
195 # @INTERNAL
196 # @DESCRIPTION:
197 # Set the eclass variables as necessary for operation. This can involve
198 # setting EGIT_* to defaults or ${PN}_LIVE_* variables.
199 _git-r3_env_setup() {
200         debug-print-function ${FUNCNAME} "$@"
201
202         # check the clone type
203         case "${EGIT_CLONE_TYPE}" in
204                 mirror|single+tags|single|shallow)
205                         ;;
206                 *)
207                         die "Invalid EGIT_CLONE_TYPE=${EGIT_CLONE_TYPE}"
208         esac
209         case "${EGIT_MIN_CLONE_TYPE}" in
210                 shallow)
211                         ;;
212                 single)
213                         if [[ ${EGIT_CLONE_TYPE} == shallow ]]; then
214                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle\e[22m' mode, adjusting"
215                                 EGIT_CLONE_TYPE=single
216                         fi
217                         ;;
218                 single+tags)
219                         if [[ ${EGIT_CLONE_TYPE} == shallow || ${EGIT_CLONE_TYPE} == single ]]; then
220                                 einfo "git-r3: ebuild needs to be cloned in '\e[1msingle+tags\e[22m' mode, adjusting"
221                                 EGIT_CLONE_TYPE=single+tags
222                         fi
223                         ;;
224                 mirror)
225                         if [[ ${EGIT_CLONE_TYPE} != mirror ]]; then
226                                 einfo "git-r3: ebuild needs to be cloned in '\e[1mmirror\e[22m' mode, adjusting"
227                                 EGIT_CLONE_TYPE=mirror
228                         fi
229                         ;;
230                 *)
231                         die "Invalid EGIT_MIN_CLONE_TYPE=${EGIT_MIN_CLONE_TYPE}"
232         esac
233
234         if [[ ${EGIT_SUBMODULES[@]+1} && $(declare -p EGIT_SUBMODULES) != "declare -a"* ]]
235         then
236                 die 'EGIT_SUBMODULES must be an array.'
237         fi
238
239         local esc_pn livevar
240         esc_pn=${PN//[-+]/_}
241
242         livevar=${esc_pn}_LIVE_REPO
243         EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}}
244         [[ ${!livevar} ]] \
245                 && ewarn "Using ${livevar}, no support will be provided"
246
247         livevar=${esc_pn}_LIVE_BRANCH
248         EGIT_BRANCH=${!livevar:-${EGIT_BRANCH}}
249         [[ ${!livevar} ]] \
250                 && ewarn "Using ${livevar}, no support will be provided"
251
252         livevar=${esc_pn}_LIVE_COMMIT
253         EGIT_COMMIT=${!livevar:-${EGIT_COMMIT}}
254         [[ ${!livevar} ]] \
255                 && ewarn "Using ${livevar}, no support will be provided"
256
257         # Migration helpers. Remove them when git-2 is removed.
258
259         if [[ ${EGIT_SOURCEDIR} ]]; then
260                 eerror "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR. While updating"
261                 eerror "your ebuild, please check whether the variable is necessary at all"
262                 eerror "since the default has been changed from \${S} to \${WORKDIR}/\${P}."
263                 eerror "Therefore, proper setting of S may be sufficient."
264                 die "EGIT_SOURCEDIR has been replaced by EGIT_CHECKOUT_DIR."
265         fi
266
267         if [[ ${EGIT_MASTER} ]]; then
268                 eerror "EGIT_MASTER has been removed. Instead, the upstream default (HEAD)"
269                 eerror "is used by the eclass. Please remove the assignment or use EGIT_BRANCH"
270                 eerror "as necessary."
271                 die "EGIT_MASTER has been removed."
272         fi
273
274         if [[ ${EGIT_HAS_SUBMODULES} ]]; then
275                 eerror "EGIT_HAS_SUBMODULES has been removed. The eclass no longer needs"
276                 eerror "to switch the clone type in order to support submodules and therefore"
277                 eerror "submodules are detected and fetched automatically. If you need to"
278                 eerror "disable or filter submodules, see EGIT_SUBMODULES."
279                 die "EGIT_HAS_SUBMODULES is no longer necessary."
280         fi
281
282         if [[ ${EGIT_PROJECT} ]]; then
283                 eerror "EGIT_PROJECT has been removed. Instead, the eclass determines"
284                 eerror "the local clone path using path in canonical EGIT_REPO_URI."
285                 eerror "If the current algorithm causes issues for you, please report a bug."
286                 die "EGIT_PROJECT is no longer necessary."
287         fi
288
289         if [[ ${EGIT_BOOTSTRAP} ]]; then
290                 eerror "EGIT_BOOTSTRAP has been removed. Please create proper src_prepare()"
291                 eerror "instead."
292                 die "EGIT_BOOTSTRAP has been removed."
293         fi
294
295         if [[ ${EGIT_NOUNPACK} ]]; then
296                 eerror "EGIT_NOUNPACK has been removed. The eclass no longer calls default"
297                 eerror "unpack function. If necessary, please declare proper src_unpack()."
298                 die "EGIT_NOUNPACK has been removed."
299         fi
300 }
301
302 # @FUNCTION: _git-r3_set_gitdir
303 # @USAGE: <repo-uri>
304 # @INTERNAL
305 # @DESCRIPTION:
306 # Obtain the local repository path and set it as GIT_DIR. Creates
307 # a new repository if necessary.
308 #
309 # <repo-uri> may be used to compose the path. It should therefore be
310 # a canonical URI to the repository.
311 _git-r3_set_gitdir() {
312         debug-print-function ${FUNCNAME} "$@"
313
314         local repo_name=${1#*://*/}
315
316         # strip the trailing slash
317         repo_name=${repo_name%/}
318
319         # strip common prefixes to make paths more likely to match
320         # e.g. git://X/Y.git vs https://X/git/Y.git
321         # (but just one of the prefixes)
322         case "${repo_name}" in
323                 # gnome.org... who else?
324                 browse/*) repo_name=${repo_name#browse/};;
325                 # cgit can proxy requests to git
326                 cgit/*) repo_name=${repo_name#cgit/};;
327                 # pretty common
328                 git/*) repo_name=${repo_name#git/};;
329                 # gentoo.org
330                 gitroot/*) repo_name=${repo_name#gitroot/};;
331                 # google code, sourceforge
332                 p/*) repo_name=${repo_name#p/};;
333                 # kernel.org
334                 pub/scm/*) repo_name=${repo_name#pub/scm/};;
335         esac
336         # ensure a .git suffix, same reason
337         repo_name=${repo_name%.git}.git
338         # now replace all the slashes
339         repo_name=${repo_name//\//_}
340
341         local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}}
342         : ${EGIT3_STORE_DIR:=${distdir}/git3-src}
343
344         GIT_DIR=${EGIT3_STORE_DIR}/${repo_name}
345
346         if [[ ! -d ${EGIT3_STORE_DIR} ]]; then
347                 (
348                         addwrite /
349                         mkdir -p "${EGIT3_STORE_DIR}"
350                 ) || die "Unable to create ${EGIT3_STORE_DIR}"
351         fi
352
353         addwrite "${EGIT3_STORE_DIR}"
354         if [[ ! -d ${GIT_DIR} ]]; then
355                 local saved_umask
356                 if [[ ${EVCS_UMASK} ]]; then
357                         saved_umask=$(umask)
358                         umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
359                 fi
360                 mkdir "${GIT_DIR}" || die
361                 git init --bare || die
362                 if [[ ${saved_umask} ]]; then
363                         umask "${saved_umask}" || die
364                 fi
365         fi
366 }
367
368 # @FUNCTION: _git-r3_set_submodules
369 # @USAGE: <file-contents>
370 # @INTERNAL
371 # @DESCRIPTION:
372 # Parse .gitmodules contents passed as <file-contents>
373 # as in "$(cat .gitmodules)"). Composes a 'submodules' array that
374 # contains in order (name, URL, path) for each submodule.
375 _git-r3_set_submodules() {
376         debug-print-function ${FUNCNAME} "$@"
377
378         local data=${1}
379
380         # ( name url path ... )
381         submodules=()
382
383         local l
384         while read l; do
385                 # submodule.<path>.path=<path>
386                 # submodule.<path>.url=<url>
387                 [[ ${l} == submodule.*.url=* ]] || continue
388
389                 l=${l#submodule.}
390                 local subname=${l%%.url=*}
391
392                 # filter out on EGIT_SUBMODULES
393                 if declare -p EGIT_SUBMODULES &>/dev/null; then
394                         local p l_res res=
395                         for p in "${EGIT_SUBMODULES[@]}"; do
396                                 if [[ ${p} == -* ]]; then
397                                         p=${p#-}
398                                         l_res=
399                                 else
400                                         l_res=1
401                                 fi
402
403                                 [[ ${subname} == ${p} ]] && res=${l_res}
404                         done
405
406                         if [[ ! ${res} ]]; then
407                                 einfo "Skipping submodule \e[1m${subname}\e[22m"
408                                 continue
409                         fi
410                 fi
411
412                 # skip modules that have 'update = none', bug #487262.
413                 local upd=$(echo "${data}" | git config -f /dev/fd/0 \
414                         submodule."${subname}".update)
415                 [[ ${upd} == none ]] && continue
416
417                 # https://github.com/git/git/blob/master/refs.c#L39
418                 # for now, we just filter /. because of #572312
419                 local enc_subname=${subname//\/.//_}
420                 [[ ${enc_subname} == .* ]] && enc_subname=_${enc_subname#.}
421
422                 submodules+=(
423                         "${enc_subname}"
424                         "$(echo "${data}" | git config -f /dev/fd/0 \
425                                 submodule."${subname}".url || die)"
426                         "$(echo "${data}" | git config -f /dev/fd/0 \
427                                 submodule."${subname}".path || die)"
428                 )
429         done < <(echo "${data}" | git config -f /dev/fd/0 -l || die)
430 }
431
432 # @FUNCTION: _git-r3_set_subrepos
433 # @USAGE: <submodule-uri> <parent-repo-uri>...
434 # @INTERNAL
435 # @DESCRIPTION:
436 # Create 'subrepos' array containing absolute (canonical) submodule URIs
437 # for the given <submodule-uri>. If the URI is relative, URIs will be
438 # constructed using all <parent-repo-uri>s. Otherwise, this single URI
439 # will be placed in the array.
440 _git-r3_set_subrepos() {
441         debug-print-function ${FUNCNAME} "$@"
442
443         local suburl=${1}
444         subrepos=( "${@:2}" )
445
446         if [[ ${suburl} == ./* || ${suburl} == ../* ]]; then
447                 # drop all possible trailing slashes for consistency
448                 subrepos=( "${subrepos[@]%%/}" )
449
450                 while true; do
451                         if [[ ${suburl} == ./* ]]; then
452                                 suburl=${suburl:2}
453                         elif [[ ${suburl} == ../* ]]; then
454                                 suburl=${suburl:3}
455
456                                 # XXX: correctness checking
457
458                                 # drop the last path component
459                                 subrepos=( "${subrepos[@]%/*}" )
460                                 # and then the trailing slashes, again
461                                 subrepos=( "${subrepos[@]%%/}" )
462                         else
463                                 break
464                         fi
465                 done
466
467                 # append the preprocessed path to the preprocessed URIs
468                 subrepos=( "${subrepos[@]/%//${suburl}}")
469         else
470                 subrepos=( "${suburl}" )
471         fi
472 }
473
474
475 # @FUNCTION: _git-r3_is_local_repo
476 # @USAGE: <repo-uri>
477 # @INTERNAL
478 # @DESCRIPTION:
479 # Determine whether the given URI specifies a local (on-disk)
480 # repository.
481 _git-r3_is_local_repo() {
482         debug-print-function ${FUNCNAME} "$@"
483
484         local uri=${1}
485
486         [[ ${uri} == file://* || ${uri} == /* ]]
487 }
488
489 # @FUNCTION: git-r3_fetch
490 # @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]]
491 # @DESCRIPTION:
492 # Fetch new commits to the local clone of repository.
493 #
494 # <repo-uri> specifies the repository URIs to fetch from, as a space-
495 # -separated list. The first URI will be used as repository group
496 # identifier and therefore must be used consistently. When not
497 # specified, defaults to ${EGIT_REPO_URI}.
498 #
499 # <remote-ref> specifies the remote ref or commit id to fetch.
500 # It is preferred to use 'refs/heads/<branch-name>' for branches
501 # and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD'
502 # for upstream default branch and hexadecimal commit SHA1. Defaults
503 # to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that
504 # is set to a non-null value.
505 #
506 # <local-id> specifies the local branch identifier that will be used to
507 # locally store the fetch result. It should be unique to multiple
508 # fetches within the repository that can be performed at the same time
509 # (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT%/*}.
510 # This default should be fine unless you are fetching multiple trees
511 # from the same repository in the same ebuild.
512 #
513 # The fetch operation will affect the EGIT_STORE only. It will not touch
514 # the working copy, nor export any environment variables.
515 # If the repository contains submodules, they will be fetched
516 # recursively.
517 git-r3_fetch() {
518         debug-print-function ${FUNCNAME} "$@"
519
520         [[ ${EVCS_OFFLINE} ]] && return
521
522         local repos
523         if [[ ${1} ]]; then
524                 repos=( ${1} )
525         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
526                 repos=( "${EGIT_REPO_URI[@]}" )
527         else
528                 repos=( ${EGIT_REPO_URI} )
529         fi
530
531         local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
532         local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
533         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
534         local local_ref=refs/git-r3/${local_id}/__main__
535
536         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
537
538         local -x GIT_DIR
539         _git-r3_set_gitdir "${repos[0]}"
540
541         # prepend the local mirror if applicable
542         if [[ ${EGIT_MIRROR_URI} ]]; then
543                 repos=(
544                         "${EGIT_MIRROR_URI%/}/${GIT_DIR##*/}"
545                         "${repos[@]}"
546                 )
547         fi
548
549         # try to fetch from the remote
550         local r success saved_umask
551         if [[ ${EVCS_UMASK} ]]; then
552                 saved_umask=$(umask)
553                 umask "${EVCS_UMASK}" || die "Bad options to umask: ${EVCS_UMASK}"
554         fi
555         for r in "${repos[@]}"; do
556                 einfo "Fetching \e[1m${r}\e[22m ..."
557
558                 local fetch_command=( git fetch "${r}" )
559                 local clone_type=${EGIT_CLONE_TYPE}
560
561                 if [[ ${r} == https://* ]] && ! ROOT=/ has_version 'dev-vcs/git[curl]'; then
562                         eerror "git-r3: fetching from https:// requested. In order to support https,"
563                         eerror "dev-vcs/git needs to be built with USE=curl. Example solution:"
564                         eerror
565                         eerror "        echo dev-vcs/git curl >> /etc/portage/package.use"
566                         eerror "        emerge -1v dev-vcs/git"
567                         die "dev-vcs/git built with USE=curl required."
568                 fi
569
570                 if [[ ${r} == https://code.google.com/* ]]; then
571                         # Google Code has special magic on top of git that:
572                         # 1) can't handle shallow clones at all,
573                         # 2) fetches duplicately when tags are pulled in with branch
574                         # so automatically switch to single+tags mode.
575                         if [[ ${clone_type} == shallow ]]; then
576                                 einfo "  Google Code does not support shallow clones"
577                                 einfo "  using \e[1mEGIT_CLONE_TYPE=single+tags\e[22m"
578                                 clone_type=single+tags
579                         elif [[ ${clone_type} == single ]]; then
580                                 einfo "  git-r3: Google Code does not send tags properly in 'single' mode"
581                                 einfo "  using \e[1mEGIT_CLONE_TYPE=single+tags\e[22m"
582                                 clone_type=single+tags
583                         fi
584                 fi
585
586                 if [[ ${clone_type} == mirror ]]; then
587                         fetch_command+=(
588                                 --prune
589                                 # mirror the remote branches as local branches
590                                 "+refs/heads/*:refs/heads/*"
591                                 # pull tags explicitly in order to prune them properly
592                                 "+refs/tags/*:refs/tags/*"
593                                 # notes in case something needs them
594                                 "+refs/notes/*:refs/notes/*"
595                                 # and HEAD in case we need the default branch
596                                 # (we keep it in refs/git-r3 since otherwise --prune interferes)
597                                 "+HEAD:refs/git-r3/HEAD"
598                         )
599                 else # single or shallow
600                         local fetch_l fetch_r
601
602                         if [[ ${remote_ref} == HEAD ]]; then
603                                 # HEAD
604                                 fetch_l=HEAD
605                         elif [[ ${remote_ref} == refs/* ]]; then
606                                 # regular branch, tag or some other explicit ref
607                                 fetch_l=${remote_ref}
608                         else
609                                 # tag or commit id...
610                                 # let ls-remote figure it out
611                                 local tagref=$(git ls-remote "${r}" "refs/tags/${remote_ref}")
612
613                                 # if it was a tag, ls-remote obtained a hash
614                                 if [[ ${tagref} ]]; then
615                                         # tag
616                                         fetch_l=refs/tags/${remote_ref}
617                                 else
618                                         # commit id
619                                         # so we need to fetch the whole branch
620                                         if [[ ${branch} ]]; then
621                                                 fetch_l=${branch}
622                                         else
623                                                 fetch_l=HEAD
624                                         fi
625
626                                         # fetching by commit in shallow mode? can't do.
627                                         if [[ ${clone_type} == shallow ]]; then
628                                                 clone_type=single
629                                         fi
630                                 fi
631                         fi
632
633                         if [[ ${fetch_l} == HEAD ]]; then
634                                 fetch_r=refs/git-r3/HEAD
635                         else
636                                 fetch_r=${fetch_l}
637                         fi
638
639                         fetch_command+=(
640                                 "+${fetch_l}:${fetch_r}"
641                         )
642
643                         if [[ ${clone_type} == single+tags ]]; then
644                                 fetch_command+=(
645                                         # pull tags explicitly as requested
646                                         "+refs/tags/*:refs/tags/*"
647                                 )
648                         fi
649                 fi
650
651                 if [[ ${clone_type} == shallow ]]; then
652                         if _git-r3_is_local_repo; then
653                                 # '--depth 1' causes sandbox violations with local repos
654                                 # bug #491260
655                                 clone_type=single
656                         elif [[ ! $(git rev-parse --quiet --verify "${fetch_r}") ]]
657                         then
658                                 # use '--depth 1' when fetching a new branch
659                                 fetch_command+=( --depth 1 )
660                         fi
661                 else # non-shallow mode
662                         if [[ -f ${GIT_DIR}/shallow ]]; then
663                                 fetch_command+=( --unshallow )
664                         fi
665                 fi
666
667                 set -- "${fetch_command[@]}"
668                 echo "${@}" >&2
669                 if "${@}"; then
670                         if [[ ${clone_type} == mirror || ${fetch_l} == HEAD ]]; then
671                                 # update our HEAD to match our remote HEAD ref
672                                 git symbolic-ref HEAD refs/git-r3/HEAD \
673                                                 || die "Unable to update HEAD"
674                         fi
675
676                         # now let's see what the user wants from us
677                         local full_remote_ref=$(
678                                 git rev-parse --verify --symbolic-full-name "${remote_ref}"
679                         )
680
681                         if [[ ${full_remote_ref} ]]; then
682                                 # when we are given a ref, create a symbolic ref
683                                 # so that we preserve the actual argument
684                                 set -- git symbolic-ref "${local_ref}" "${full_remote_ref}"
685                         else
686                                 # otherwise, we were likely given a commit id
687                                 set -- git update-ref --no-deref "${local_ref}" "${remote_ref}"
688                         fi
689
690                         echo "${@}" >&2
691                         if ! "${@}"; then
692                                 die "Referencing ${remote_ref} failed (wrong ref?)."
693                         fi
694
695                         success=1
696                         break
697                 fi
698         done
699         if [[ ${saved_umask} ]]; then
700                 umask "${saved_umask}" || die
701         fi
702         [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI"
703
704         # submodules can reference commits in any branch
705         # always use the 'mirror' mode to accomodate that, bug #503332
706         local EGIT_CLONE_TYPE=mirror
707
708         # recursively fetch submodules
709         if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then
710                 local submodules
711                 _git-r3_set_submodules \
712                         "$(git cat-file -p "${local_ref}":.gitmodules || die)"
713
714                 while [[ ${submodules[@]} ]]; do
715                         local subname=${submodules[0]}
716                         local url=${submodules[1]}
717                         local path=${submodules[2]}
718
719                         # use only submodules for which path does exist
720                         # (this is in par with 'git submodule'), bug #551100
721                         # note: git cat-file does not work for submodules
722                         if [[ $(git ls-tree -d "${local_ref}" "${path}") ]]
723                         then
724                                 local commit=$(git rev-parse "${local_ref}:${path}" || die)
725
726                                 if [[ ! ${commit} ]]; then
727                                         die "Unable to get commit id for submodule ${subname}"
728                                 fi
729
730                                 local subrepos
731                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
732
733                                 git-r3_fetch "${subrepos[*]}" "${commit}" "${local_id}/${subname}"
734                         fi
735
736                         submodules=( "${submodules[@]:3}" ) # shift
737                 done
738         fi
739 }
740
741 # @FUNCTION: git-r3_checkout
742 # @USAGE: [<repo-uri> [<checkout-path> [<local-id>]]]
743 # @DESCRIPTION:
744 # Check the previously fetched tree to the working copy.
745 #
746 # <repo-uri> specifies the repository URIs, as a space-separated list.
747 # The first URI will be used as repository group identifier
748 # and therefore must be used consistently with git-r3_fetch.
749 # The remaining URIs are not used and therefore may be omitted.
750 # When not specified, defaults to ${EGIT_REPO_URI}.
751 #
752 # <checkout-path> specifies the path to place the checkout. It defaults
753 # to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}.
754 #
755 # <local-id> needs to specify the local identifier that was used
756 # for respective git-r3_fetch.
757 #
758 # The checkout operation will write to the working copy, and export
759 # the repository state into the environment. If the repository contains
760 # submodules, they will be checked out recursively.
761 git-r3_checkout() {
762         debug-print-function ${FUNCNAME} "$@"
763
764         local repos
765         if [[ ${1} ]]; then
766                 repos=( ${1} )
767         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
768                 repos=( "${EGIT_REPO_URI[@]}" )
769         else
770                 repos=( ${EGIT_REPO_URI} )
771         fi
772
773         local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}}
774         local local_id=${3:-${CATEGORY}/${PN}/${SLOT%/*}}
775
776         local -x GIT_DIR
777         _git-r3_set_gitdir "${repos[0]}"
778
779         einfo "Checking out \e[1m${repos[0]}\e[22m to \e[1m${out_dir}\e[22m ..."
780
781         if ! git cat-file -e refs/git-r3/"${local_id}"/__main__; then
782                 if [[ ${EVCS_OFFLINE} ]]; then
783                         die "No local clone of ${repos[0]}. Unable to work with EVCS_OFFLINE."
784                 else
785                         die "Logic error: no local clone of ${repos[0]}. git-r3_fetch not used?"
786                 fi
787         fi
788         local remote_ref=$(
789                 git symbolic-ref --quiet refs/git-r3/"${local_id}"/__main__
790         )
791         local new_commit_id=$(
792                 git rev-parse --verify refs/git-r3/"${local_id}"/__main__
793         )
794
795         git-r3_sub_checkout() {
796                 local orig_repo=${GIT_DIR}
797                 local -x GIT_DIR=${out_dir}/.git
798                 local -x GIT_WORK_TREE=${out_dir}
799
800                 mkdir -p "${out_dir}" || die
801
802                 # use git init+fetch instead of clone since the latter doesn't like
803                 # non-empty directories.
804
805                 git init --quiet || die
806                 # setup 'alternates' to avoid copying objects
807                 echo "${orig_repo}/objects" > "${GIT_DIR}"/objects/info/alternates || die
808                 # now copy the refs
809                 cp -R "${orig_repo}"/refs/* "${GIT_DIR}"/refs/ || die
810
811                 # (no need to copy HEAD, we will set it via checkout)
812
813                 if [[ -f ${orig_repo}/shallow ]]; then
814                         cp "${orig_repo}"/shallow "${GIT_DIR}"/ || die
815                 fi
816
817                 set -- git checkout --quiet
818                 if [[ ${remote_ref} ]]; then
819                         set -- "${@}" "${remote_ref#refs/heads/}"
820                 else
821                         set -- "${@}" "${new_commit_id}"
822                 fi
823                 echo "${@}" >&2
824                 "${@}" || die "git checkout ${remote_ref:-${new_commit_id}} failed"
825         }
826         git-r3_sub_checkout
827         unset -f git-r3_sub_checkout
828
829         local old_commit_id=$(
830                 git rev-parse --quiet --verify refs/git-r3/"${local_id}"/__old__
831         )
832         if [[ ! ${old_commit_id} ]]; then
833                 echo "GIT NEW branch -->"
834                 echo "   repository:               ${repos[0]}"
835                 echo "   at the commit:            ${new_commit_id}"
836         else
837                 # diff against previous revision
838                 echo "GIT update -->"
839                 echo "   repository:               ${repos[0]}"
840                 # write out message based on the revisions
841                 if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then
842                         echo "   updating from commit:     ${old_commit_id}"
843                         echo "   to commit:                ${new_commit_id}"
844
845                         git --no-pager diff --stat \
846                                 ${old_commit_id}..${new_commit_id}
847                 else
848                         echo "   at the commit:            ${new_commit_id}"
849                 fi
850         fi
851         git update-ref --no-deref refs/git-r3/"${local_id}"/{__old__,__main__} || die
852
853         # recursively checkout submodules
854         if [[ -f ${out_dir}/.gitmodules ]]; then
855                 local submodules
856                 _git-r3_set_submodules \
857                         "$(<"${out_dir}"/.gitmodules)"
858
859                 while [[ ${submodules[@]} ]]; do
860                         local subname=${submodules[0]}
861                         local url=${submodules[1]}
862                         local path=${submodules[2]}
863
864                         # use only submodules for which path does exist
865                         # (this is in par with 'git submodule'), bug #551100
866                         if [[ -d ${out_dir}/${path} ]]; then
867                                 local subrepos
868                                 _git-r3_set_subrepos "${url}" "${repos[@]}"
869
870                                 git-r3_checkout "${subrepos[*]}" "${out_dir}/${path}" \
871                                         "${local_id}/${subname}"
872                         fi
873
874                         submodules=( "${submodules[@]:3}" ) # shift
875                 done
876         fi
877
878         # keep this *after* submodules
879         export EGIT_DIR=${GIT_DIR}
880         export EGIT_VERSION=${new_commit_id}
881 }
882
883 # @FUNCTION: git-r3_peek_remote_ref
884 # @USAGE: [<repo-uri> [<remote-ref>]]
885 # @DESCRIPTION:
886 # Peek the reference in the remote repository and print the matching
887 # (newest) commit SHA1.
888 #
889 # <repo-uri> specifies the repository URIs to fetch from, as a space-
890 # -separated list. When not specified, defaults to ${EGIT_REPO_URI}.
891 #
892 # <remote-ref> specifies the remote ref to peek.  It is preferred to use
893 # 'refs/heads/<branch-name>' for branches and 'refs/tags/<tag-name>'
894 # for tags. Alternatively, 'HEAD' may be used for upstream default
895 # branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal
896 # 'HEAD' that is set to a non-null value.
897 #
898 # The operation will be done purely on the remote, without using local
899 # storage. If commit SHA1 is provided as <remote-ref>, the function will
900 # fail due to limitations of git protocol.
901 #
902 # On success, the function returns 0 and writes hexadecimal commit SHA1
903 # to stdout. On failure, the function returns 1.
904 git-r3_peek_remote_ref() {
905         debug-print-function ${FUNCNAME} "$@"
906
907         local repos
908         if [[ ${1} ]]; then
909                 repos=( ${1} )
910         elif [[ $(declare -p EGIT_REPO_URI) == "declare -a"* ]]; then
911                 repos=( "${EGIT_REPO_URI[@]}" )
912         else
913                 repos=( ${EGIT_REPO_URI} )
914         fi
915
916         local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
917         local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
918
919         [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
920
921         local r success
922         for r in "${repos[@]}"; do
923                 einfo "Peeking \e[1m${remote_ref}\e[22m on \e[1m${r}\e[22m ..." >&2
924
925                 local lookup_ref
926                 if [[ ${remote_ref} == refs/* || ${remote_ref} == HEAD ]]
927                 then
928                         lookup_ref=${remote_ref}
929                 else
930                         # ls-remote by commit is going to fail anyway,
931                         # so we may as well pass refs/tags/ABCDEF...
932                         lookup_ref=refs/tags/${remote_ref}
933                 fi
934
935                 # split on whitespace
936                 local ref=(
937                         $(git ls-remote "${r}" "${lookup_ref}")
938                 )
939
940                 if [[ ${ref[0]} ]]; then
941                         echo "${ref[0]}"
942                         return 0
943                 fi
944         done
945
946         return 1
947 }
948
949 git-r3_src_fetch() {
950         debug-print-function ${FUNCNAME} "$@"
951
952         if [[ ! ${EGIT3_STORE_DIR} && ${EGIT_STORE_DIR} ]]; then
953                 ewarn "You have set EGIT_STORE_DIR but not EGIT3_STORE_DIR. Please consider"
954                 ewarn "setting EGIT3_STORE_DIR for git-r3.eclass. It is recommended to use"
955                 ewarn "a different directory than EGIT_STORE_DIR to ease removing old clones"
956                 ewarn "when git-2 eclass becomes deprecated."
957         fi
958
959         _git-r3_env_setup
960         git-r3_fetch
961 }
962
963 git-r3_src_unpack() {
964         debug-print-function ${FUNCNAME} "$@"
965
966         _git-r3_env_setup
967         git-r3_src_fetch
968         git-r3_checkout
969 }
970
971 # https://bugs.gentoo.org/show_bug.cgi?id=482666
972 git-r3_pkg_needrebuild() {
973         debug-print-function ${FUNCNAME} "$@"
974
975         local new_commit_id=$(git-r3_peek_remote_ref)
976         [[ ${new_commit_id} && ${EGIT_VERSION} ]] || die "Lookup failed"
977
978         if [[ ${EGIT_VERSION} != ${new_commit_id} ]]; then
979                 einfo "Update from \e[1m${EGIT_VERSION}\e[22m to \e[1m${new_commit_id}\e[22m"
980         else
981                 einfo "Local and remote at \e[1m${EGIT_VERSION}\e[22m"
982         fi
983
984         [[ ${EGIT_VERSION} != ${new_commit_id} ]]
985 }
986
987 # 'export' locally until this gets into EAPI
988 pkg_needrebuild() { git-r3_pkg_needrebuild; }
989
990 _GIT_R3=1
991 fi