A few more changes. 1. Added EGIT_CHECKOUT_DIR to control the place where stuff is checked out.
2. Moved the defaults from src_fetch() and src_unpack() into git-r3_fetch() and git-r3_checkout(). This makes using them easier, like: git-r3_fetch http://repo1 git-r3_fetch http://repo2 git-r3_checkout http://repo1 git-r3_checkout http://repo2 ${S}/foo 3. Added prototype pkg_isuptodate() function [bug 482666 [1]]. (I will update it when we decide on how the func should be named :)) As an additional testing tool, I attached the version of git-r3 converted to git-2 compatible API. IOW, a drop-in replacement that you could use to test the new eclass internals with API almost fully compatible with the old one. [1]:https://bugs.gentoo.org/show_bug.cgi?id=482666 -- Best regards, Michał Górny
diff --git a/gx86/eclass/git-r3.eclass b/gx86/eclass/git-r3.eclass
index d56c818..84c04c7 100644
--- a/gx86/eclass/git-r3.eclass
+++ b/gx86/eclass/git-r3.eclass
@@ -71,6 +71,12 @@ if [[ ! ${_GIT_R3} ]]; then
#
# It can be overriden via env using ${PN}_LIVE_COMMIT variable.
+# @ECLASS-VARIABLE: EGIT_CHECKOUT_DIR
+# @DESCRIPTION:
+# The directory to check the git sources out to.
+#
+# EGIT_CHECKOUT_DIR=${WORKDIR}/${P}
+
# @ECLASS-VARIABLE: EGIT_NONSHALLOW
# @DEFAULT_UNSET
# @DESCRIPTION:
@@ -94,8 +100,6 @@ _git-r3_env_setup() {
livevar=${esc_pn}_LIVE_REPO
EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}}
- [[ ${EGIT_REPO_URI} ]] \
- || die "EGIT_REPO_URI must be set to a non-empty value"
[[ ${!livevar} ]] \
&& ewarn "Using ${livevar}, no support will be provided"
@@ -209,26 +213,44 @@ _git-r3_set_submodules() {
}
# @FUNCTION: git-r3_fetch
-# @USAGE: <repo-uri> <remote-ref> <local-id>
+# @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]]
# @DESCRIPTION:
-# Fetch new commits to the local clone of repository. <repo-uri> follows
-# the syntax of EGIT_REPO_URI and may list multiple (fallback) URIs.
-# <remote-ref> specifies the remote ref to fetch (branch, tag
-# or commit). <local-id> specifies an identifier that needs to uniquely
-# identify the fetch operation in case multiple parallel merges used
-# the git repo. <local-id> usually involves using CATEGORY, PN and SLOT.
+# Fetch new commits to the local clone of repository.
+#
+# <repo-uri> specifies the repository URIs to fetch from, as a space-
+# -separated list. The first URI will be used as repository group
+# identifier and therefore must be used consistently. When not
+# specified, defaults to ${EGIT_REPO_URI}.
#
-# The fetch operation will only affect the local storage. It will not
-# touch the working copy. If the repository contains submodules, they
-# will be fetched recursively as well.
+# <remote-ref> specifies the remote ref or commit id to fetch.
+# It is preferred to use 'refs/heads/<branch-name>' for branches
+# and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD'
+# for upstream default branch and hexadecimal commit SHA1. Defaults
+# to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that
+# is set to a non-null value.
+#
+# <local-id> specifies the local branch identifier that will be used to
+# locally store the fetch result. It should be unique to multiple
+# fetches within the repository that can be performed at the same time
+# (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT}.
+# This default should be fine unless you are fetching multiple trees
+# from the same repository in the same ebuild.
+#
+# The fetch operation will affect the EGIT_STORE only. It will not touch
+# the working copy, nor export any environment variables.
+# If the repository contains submodules, they will be fetched
+# recursively.
git-r3_fetch() {
debug-print-function ${FUNCNAME} "$@"
- local repos=( ${1} )
- local remote_ref=${2}
- local local_id=${3}
+ local repos=( ${1:-${EGIT_REPO_URI}} )
+ local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
+ local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
+ local local_id=${3:-${CATEGORY}/${PN}/${SLOT}}
local local_ref=refs/heads/${local_id}/__main__
+ [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
+
local -x GIT_DIR
_git-r3_set_gitdir ${repos[0]}
@@ -282,7 +304,7 @@ git-r3_fetch() {
if [[ ${is_branch} ]]; then
ref_param+=( -f "${remote_ref}:${local_id}/__main__" )
else
- ref_param+=( "${remote_ref}" )
+ ref_param+=( "refs/tags/${remote_ref}" )
fi
fi
@@ -331,22 +353,31 @@ git-r3_fetch() {
}
# @FUNCTION: git-r3_checkout
-# @USAGE: <repo-uri> <local-id> <path>
+# @USAGE: [<repo-uri> [<checkout-path> [<local-id>]]]
# @DESCRIPTION:
-# Check the previously fetched commit out to <path> (usually
-# ${WORKDIR}/${P}). <repo-uri> follows the syntax of EGIT_REPO_URI
-# and will be used to re-construct the local storage path. <local-id>
-# is the unique identifier used for the fetch operation and will
-# be used to obtain the proper commit.
+# Check the previously fetched tree to the working copy.
+#
+# <repo-uri> specifies the repository URIs, as a space-separated list.
+# The first URI will be used as repository group identifier
+# and therefore must be used consistently with git-r3_fetch.
+# The remaining URIs are not used and therefore may be omitted.
+# When not specified, defaults to ${EGIT_REPO_URI}.
+#
+# <checkout-path> specifies the path to place the checkout. It defaults
+# to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}.
#
-# If the repository contains submodules, they will be checked out
-# recursively as well.
+# <local-id> needs to specify the local identifier that was used
+# for respective git-r3_fetch.
+#
+# The checkout operation will write to the working copy, and export
+# the repository state into the environment. If the repository contains
+# submodules, they will be checked out recursively.
git-r3_checkout() {
debug-print-function ${FUNCNAME} "$@"
- local repos=( ${1} )
- local local_id=${2}
- local out_dir=${3}
+ local repos=( ${1:-${EGIT_REPO_URI}} )
+ local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}}
+ local local_id=${3:-${CATEGORY}/${PN}/${SLOT}}
local -x GIT_DIR GIT_WORK_TREE
_git-r3_set_gitdir ${repos[0]}
@@ -396,8 +427,8 @@ git-r3_checkout() {
local url=${submodules[1]}
local path=${submodules[2]}
- git-r3_checkout "${url}" "${local_id}/${subname}" \
- "${GIT_WORK_TREE}/${path}"
+ git-r3_checkout "${url}" "${GIT_WORK_TREE}/${path}" \
+ "${local_id}/${subname}"
submodules=( "${submodules[@]:3}" ) # shift
done
@@ -408,16 +439,72 @@ git-r3_checkout() {
export EGIT_VERSION=${new_commit_id}
}
+# @FUNCTION: git-r3_peek_remote_ref
+# @USAGE: [<repo-uri> [<remote-ref>]]
+# @DESCRIPTION:
+# Peek the reference in the remote repository and print the matching
+# (newest) commit SHA1.
+#
+# <repo-uri> specifies the repository URIs to fetch from, as a space-
+# -separated list. When not specified, defaults to ${EGIT_REPO_URI}.
+#
+# <remote-ref> specifies the remote ref to peek. It is preferred to use
+# 'refs/heads/<branch-name>' for branches and 'refs/tags/<tag-name>'
+# for tags. Alternatively, 'HEAD' may be used for upstream default
+# branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal
+# 'HEAD' that is set to a non-null value.
+#
+# The operation will be done purely on the remote, without using local
+# storage. If commit SHA1 is provided as <remote-ref>, the function will
+# fail due to limitations of git protocol.
+#
+# On success, the function returns 0 and writes hexadecimal commit SHA1
+# to stdout. On failure, the function returns 1.
+git-r3_peek_remote_ref() {
+ debug-print-function ${FUNCNAME} "$@"
+
+ local repos=( ${1:-${EGIT_REPO_URI}} )
+ local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
+ local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}}
+
+ [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset"
+
+ local r success
+ for r in ${repos[@]}; do
+ einfo "Peeking ${remote_ref} on ${r} ..." >&2
+
+ local is_branch lookup_ref
+ if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]]
+ then
+ is_branch=1
+ lookup_ref=${remote_ref}
+ else
+ # ls-remote by commit is going to fail anyway,
+ # so we may as well pass refs/tags/ABCDEF...
+ lookup_ref=refs/tags/${remote_ref}
+ fi
+
+ # split on whitespace
+ local ref=(
+ $(git ls-remote "${r}" "${lookup_ref}")
+ )
+
+ if [[ ${ref[0]} ]]; then
+ echo "${ref[0]}"
+ return 0
+ fi
+ done
+
+ return 1
+}
+
git-r3_src_fetch() {
debug-print-function ${FUNCNAME} "$@"
[[ ${EVCS_OFFLINE} ]] && return
_git-r3_env_setup
- local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}}
- git-r3_fetch "${EGIT_REPO_URI}" \
- "${EGIT_COMMIT:-${branch:-HEAD}}" \
- ${CATEGORY}/${PN}/${SLOT}
+ git-r3_fetch
}
git-r3_src_unpack() {
@@ -425,9 +512,16 @@ git-r3_src_unpack() {
_git-r3_env_setup
git-r3_src_fetch
- git-r3_checkout "${EGIT_REPO_URI}" \
- ${CATEGORY}/${PN}/${SLOT} \
- "${WORKDIR}/${P}"
+ git-r3_checkout
+}
+
+git-r3_pkg_isuptodate() {
+ debug-print-function ${FUNCNAME} "$@"
+
+ local new_commit_id=$(git-r3_peek_remote_ref)
+ ewarn "old: ${EGIT_VERSION}"
+ ewarn "new: ${new_commit_id}"
+ [[ ! ${new_commit_id} || ${EGIT_VERSION} == ${new_commit_id} ]]
}
_GIT_R3=1
# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ # @ECLASS: git-r3.eclass # @MAINTAINER: # MichaŠGórny <[email protected]> # @BLURB: Eclass for fetching and unpacking git repositories. # @DESCRIPTION: # Third generation eclass for easing maitenance of live ebuilds using # git as remote repository. Eclass supports lightweight (shallow) # clones, local object deduplication and submodules. case "${EAPI:-0}" in 0|1|2|3|4|5) ;; *) die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;; esac if [[ ! ${_GIT_R3} ]]; then inherit eutils fi EXPORT_FUNCTIONS src_unpack if [[ ! ${_GIT_R3} ]]; then # @ECLASS-VARIABLE: EGIT_STORE_DIR # @DESCRIPTION: # Storage directory for git sources. # # EGIT_STORE_DIR=${DISTDIR}/git3-src # @ECLASS-VARIABLE: EGIT_REPO_URI # @REQUIRED # @DESCRIPTION: # URIs to the repository, e.g. git://foo, https://foo. If multiple URIs # are provided, the eclass will consider them as fallback URIs to try # if the first URI does not work. # # It can be overriden via env using ${PN}_LIVE_REPO variable. # # Example: # @CODE # EGIT_REPO_URI="git://a/b.git https://c/d.git" # @CODE # @ECLASS-VARIABLE: EVCS_OFFLINE # @DEFAULT_UNSET # @DESCRIPTION: # If non-empty, this variable prevents any online operations. # @ECLASS-VARIABLE: EGIT_BRANCH # @DEFAULT_UNSET # @DESCRIPTION: # The branch name to check out. If unset, the upstream default (HEAD) # will be used. # # It can be overriden via env using ${PN}_LIVE_BRANCH variable. # @ECLASS-VARIABLE: EGIT_COMMIT # @DEFAULT_UNSET # @DESCRIPTION: # The tag name or commit identifier to check out. If unset, newest # commit from the branch will be used. If set, EGIT_BRANCH will # be ignored. # # It can be overriden via env using ${PN}_LIVE_COMMIT variable. # @ECLASS-VARIABLE: EGIT_CHECKOUT_DIR # @DESCRIPTION: # The directory to check the git sources out to. # # EGIT_CHECKOUT_DIR=${WORKDIR}/${P} # @ECLASS-VARIABLE: EGIT_NONSHALLOW # @DEFAULT_UNSET # @DESCRIPTION: # Disable performing shallow fetches/clones. Shallow clones have # a fair number of limitations. Therefore, if you'd like the eclass to # perform complete clones instead, set this to a non-null value. # # This variable is to be set in make.conf. Ebuilds are not allowed # to set it. # @FUNCTION: _git-r3_env_setup # @INTERNAL # @DESCRIPTION: # Set the eclass variables as necessary for operation. This can involve # setting EGIT_* to defaults or ${PN}_LIVE_* variables. _git-r3_env_setup() { debug-print-function ${FUNCNAME} "$@" local esc_pn livevar esc_pn=${PN//[-+]/_} livevar=${esc_pn}_LIVE_REPO EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" livevar=${esc_pn}_LIVE_BRANCH EGIT_BRANCH=${!livevar:-${EGIT_BRANCH}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" livevar=${esc_pn}_LIVE_COMMIT EGIT_COMMIT=${!livevar:-${EGIT_COMMIT}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" # git-2 unsupported cruft local v for v in EGIT_{SOURCEDIR,MASTER,HAS_SUBMODULES,PROJECT} \ EGIT_{NOUNPACK,BOOTSTRAP} do [[ ${!v} ]] && die "${v} is not supported." done } # @FUNCTION: _git-r3_set_gitdir # @USAGE: <repo-uri> # @INTERNAL # @DESCRIPTION: # Obtain the local repository path and set it as GIT_DIR. Creates # a new repository if necessary. # # <repo-uri> may be used to compose the path. It should therefore be # a canonical URI to the repository. _git-r3_set_gitdir() { debug-print-function ${FUNCNAME} "$@" local repo_name=${1#*://*/} # strip common prefixes to make paths more likely to match # e.g. git://X/Y.git vs https://X/git/Y.git # (but just one of the prefixes) case "${repo_name}" in # cgit can proxy requests to git cgit/*) repo_name=${repo_name#cgit/};; # pretty common git/*) repo_name=${repo_name#git/};; # gentoo.org gitroot/*) repo_name=${repo_name#gitroot/};; # google code, sourceforge p/*) repo_name=${repo_name#p/};; # kernel.org pub/scm/*) repo_name=${repo_name#pub/scm/};; esac # ensure a .git suffix, same reason repo_name=${repo_name%.git}.git # now replace all the slashes repo_name=${repo_name//\//_} local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}} : ${EGIT_STORE_DIR:=${distdir}/git3-src} GIT_DIR=${EGIT_STORE_DIR}/${repo_name} if [[ ! -d ${EGIT_STORE_DIR} ]]; then ( addwrite / mkdir -m0755 -p "${EGIT_STORE_DIR}" ) || die "Unable to create ${EGIT_STORE_DIR}" fi addwrite "${EGIT_STORE_DIR}" if [[ ! -d ${GIT_DIR} ]]; then mkdir "${GIT_DIR}" || die git init --bare || die # avoid auto-unshallow :) touch "${GIT_DIR}"/shallow || die fi } # @FUNCTION: _git-r3_set_submodules # @USAGE: <file-contents> # @INTERNAL # @DESCRIPTION: # Parse .gitmodules contents passed as <file-contents> # as in "$(cat .gitmodules)"). Composes a 'submodules' array that # contains in order (name, URL, path) for each submodule. _git-r3_set_submodules() { debug-print-function ${FUNCNAME} "$@" local data=${1} # ( name url path ... ) submodules=() local l while read l; do # submodule.<path>.path=<path> # submodule.<path>.url=<url> [[ ${l} == submodule.*.url=* ]] || continue l=${l#submodule.} local subname=${l%%.url=*} submodules+=( "${subname}" "$(echo "${data}" | git config -f /dev/fd/0 \ submodule."${subname}".url)" "$(echo "${data}" | git config -f /dev/fd/0 \ submodule."${subname}".path)" ) done < <(echo "${data}" | git config -f /dev/fd/0 -l) } # @FUNCTION: git-r3_fetch # @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]] # @DESCRIPTION: # Fetch new commits to the local clone of repository. # # <repo-uri> specifies the repository URIs to fetch from, as a space- # -separated list. The first URI will be used as repository group # identifier and therefore must be used consistently. When not # specified, defaults to ${EGIT_REPO_URI}. # # <remote-ref> specifies the remote ref or commit id to fetch. # It is preferred to use 'refs/heads/<branch-name>' for branches # and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD' # for upstream default branch and hexadecimal commit SHA1. Defaults # to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that # is set to a non-null value. # # <local-id> specifies the local branch identifier that will be used to # locally store the fetch result. It should be unique to multiple # fetches within the repository that can be performed at the same time # (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT}. # This default should be fine unless you are fetching multiple trees # from the same repository in the same ebuild. # # The fetch operation will affect the EGIT_STORE only. It will not touch # the working copy, nor export any environment variables. # If the repository contains submodules, they will be fetched # recursively. git-r3_fetch() { debug-print-function ${FUNCNAME} "$@" local repos=( ${1:-${EGIT_REPO_URI}} ) local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}} local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}} local local_id=${3:-${CATEGORY}/${PN}/${SLOT}} local local_ref=refs/heads/${local_id}/__main__ [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset" local -x GIT_DIR _git-r3_set_gitdir ${repos[0]} # try to fetch from the remote local r success for r in ${repos[@]}; do einfo "Fetching ${remote_ref} from ${r} ..." local is_branch lookup_ref if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]] then is_branch=1 lookup_ref=${remote_ref} else # ls-remote by commit is going to fail anyway, # so we may as well pass refs/tags/ABCDEF... lookup_ref=refs/tags/${remote_ref} fi # first, try ls-remote to see if ${remote_ref} is a real ref # and not a commit id. if it succeeds, we can pass ${remote_ref} # to 'fetch'. otherwise, we will just fetch everything # split on whitespace local ref=( $(git ls-remote "${r}" "${lookup_ref}") ) # now, another important thing. we may only fetch a remote # branch directly to a local branch. Otherwise, we need to fetch # the commit and re-create the branch on top of it. local ref_param=() if [[ ! ${ref[0]} ]]; then local EGIT_NONSHALLOW=1 fi if [[ ! -f ${GIT_DIR}/shallow ]]; then # if it's a complete repo, fetch it as-is : elif [[ ${EGIT_NONSHALLOW} ]]; then # if it's a shallow clone but we need complete, # unshallow it ref_param+=( --unshallow ) else # otherwise, just fetch as shallow ref_param+=( --depth 1 ) fi if [[ ${ref[0]} ]]; then if [[ ${is_branch} ]]; then ref_param+=( -f "${remote_ref}:${local_id}/__main__" ) else ref_param+=( "refs/tags/${remote_ref}" ) fi fi # if ${remote_ref} is branch or tag, ${ref[@]} will contain # the respective commit id. otherwise, it will be an empty # array, so the following won't evaluate to a parameter. set -- git fetch --no-tags "${r}" "${ref_param[@]}" echo "${@}" >&2 if "${@}"; then if [[ ! ${is_branch} ]]; then set -- git branch -f "${local_id}/__main__" \ "${ref[0]:-${remote_ref}}" echo "${@}" >&2 if ! "${@}"; then die "Creating branch for ${remote_ref} failed (wrong ref?)." fi fi success=1 break fi done [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI" # recursively fetch submodules if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then local submodules _git-r3_set_submodules \ "$(git cat-file -p "${local_ref}":.gitmodules || die)" while [[ ${submodules[@]} ]]; do local subname=${submodules[0]} local url=${submodules[1]} local path=${submodules[2]} local commit=$(git rev-parse "${local_ref}:${path}") if [[ ! ${commit} ]]; then die "Unable to get commit id for submodule ${subname}" fi git-r3_fetch "${url}" "${commit}" "${local_id}/${subname}" submodules=( "${submodules[@]:3}" ) # shift done fi } # @FUNCTION: git-r3_checkout # @USAGE: [<repo-uri> [<checkout-path> [<local-id>]]] # @DESCRIPTION: # Check the previously fetched tree to the working copy. # # <repo-uri> specifies the repository URIs, as a space-separated list. # The first URI will be used as repository group identifier # and therefore must be used consistently with git-r3_fetch. # The remaining URIs are not used and therefore may be omitted. # When not specified, defaults to ${EGIT_REPO_URI}. # # <checkout-path> specifies the path to place the checkout. It defaults # to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}. # # <local-id> needs to specify the local identifier that was used # for respective git-r3_fetch. # # The checkout operation will write to the working copy, and export # the repository state into the environment. If the repository contains # submodules, they will be checked out recursively. git-r3_checkout() { debug-print-function ${FUNCNAME} "$@" local repos=( ${1:-${EGIT_REPO_URI}} ) local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}} local local_id=${3:-${CATEGORY}/${PN}/${SLOT}} local -x GIT_DIR GIT_WORK_TREE _git-r3_set_gitdir ${repos[0]} GIT_WORK_TREE=${out_dir} einfo "Checking out ${repos[0]} to ${out_dir} ..." mkdir -p "${GIT_WORK_TREE}" set -- git checkout -f "${local_id}"/__main__ . echo "${@}" >&2 "${@}" || die "git checkout ${local_id}/__main__ failed" # diff against previous revision (if any) local new_commit_id=$(git rev-parse --verify "${local_id}"/__main__) local old_commit_id=$( git rev-parse --verify "${local_id}"/__old__ 2>/dev/null ) if [[ ! ${old_commit_id} ]]; then echo "GIT NEW branch -->" echo " repository: ${repos[0]}" echo " at the commit: ${new_commit_id}" else echo "GIT update -->" echo " repository: ${repos[0]}" # write out message based on the revisions if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then echo " updating from commit: ${old_commit_id}" echo " to commit: ${new_commit_id}" git --no-pager diff --color --stat \ ${old_commit_id}..${new_commit_id} else echo " at the commit: ${new_commit_id}" fi fi git branch -f "${local_id}"/{__old__,__main__} || die # recursively checkout submodules if [[ -f ${GIT_WORK_TREE}/.gitmodules ]]; then local submodules _git-r3_set_submodules \ "$(cat "${GIT_WORK_TREE}"/.gitmodules)" while [[ ${submodules[@]} ]]; do local subname=${submodules[0]} local url=${submodules[1]} local path=${submodules[2]} git-r3_checkout "${url}" "${GIT_WORK_TREE}/${path}" \ "${local_id}/${subname}" submodules=( "${submodules[@]:3}" ) # shift done fi # keep this *after* submodules export EGIT_DIR=${GIT_DIR} export EGIT_VERSION=${new_commit_id} } # @FUNCTION: git-r3_peek_remote_ref # @USAGE: [<repo-uri> [<remote-ref>]] # @DESCRIPTION: # Peek the reference in the remote repository and print the matching # (newest) commit SHA1. # # <repo-uri> specifies the repository URIs to fetch from, as a space- # -separated list. When not specified, defaults to ${EGIT_REPO_URI}. # # <remote-ref> specifies the remote ref to peek. It is preferred to use # 'refs/heads/<branch-name>' for branches and 'refs/tags/<tag-name>' # for tags. Alternatively, 'HEAD' may be used for upstream default # branch. Defaults to the first of EGIT_COMMIT, EGIT_BRANCH or literal # 'HEAD' that is set to a non-null value. # # The operation will be done purely on the remote, without using local # storage. If commit SHA1 is provided as <remote-ref>, the function will # fail due to limitations of git protocol. # # On success, the function returns 0 and writes hexadecimal commit SHA1 # to stdout. On failure, the function returns 1. git-r3_peek_remote_ref() { debug-print-function ${FUNCNAME} "$@" local repos=( ${1:-${EGIT_REPO_URI}} ) local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}} local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}} [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset" local r success for r in ${repos[@]}; do einfo "Peeking ${remote_ref} on ${r} ..." >&2 local is_branch lookup_ref if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]] then is_branch=1 lookup_ref=${remote_ref} else # ls-remote by commit is going to fail anyway, # so we may as well pass refs/tags/ABCDEF... lookup_ref=refs/tags/${remote_ref} fi # split on whitespace local ref=( $(git ls-remote "${r}" "${lookup_ref}") ) if [[ ${ref[0]} ]]; then echo "${ref[0]}" return 0 fi done return 1 } git-r3_src_fetch() { debug-print-function ${FUNCNAME} "$@" [[ ${EVCS_OFFLINE} ]] && return _git-r3_env_setup git-r3_fetch } git-r3_src_unpack() { debug-print-function ${FUNCNAME} "$@" _git-r3_env_setup git-r3_src_fetch git-r3_checkout } git-r3_pkg_isuptodate() { debug-print-function ${FUNCNAME} "$@" local new_commit_id=$(git-r3_peek_remote_ref) ewarn "old: ${EGIT_VERSION}" ewarn "new: ${new_commit_id}" [[ ! ${new_commit_id} || ${EGIT_VERSION} == ${new_commit_id} ]] } _GIT_R3=1 fi
# Copyright 1999-2013 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: $ # @ECLASS: git-r3.eclass # @MAINTAINER: # MichaŠGórny <[email protected]> # @BLURB: Eclass for fetching and unpacking git repositories. # @DESCRIPTION: # Third generation eclass for easing maitenance of live ebuilds using # git as remote repository. Eclass supports lightweight (shallow) # clones, local object deduplication and submodules. case "${EAPI:-0}" in 0|1|2|3|4|5) ;; *) die "Unsupported EAPI=${EAPI} (unknown) for ${ECLASS}" ;; esac if [[ ! ${_GIT_R3} ]]; then inherit eutils fi EXPORT_FUNCTIONS src_unpack if [[ ! ${_GIT_R3} ]]; then # @ECLASS-VARIABLE: EGIT_STORE_DIR # @DESCRIPTION: # Storage directory for git sources. # # EGIT_STORE_DIR=${DISTDIR}/git3-src # @ECLASS-VARIABLE: EGIT_REPO_URI # @REQUIRED # @DESCRIPTION: # URIs to the repository, e.g. git://foo, https://foo. If multiple URIs # are provided, the eclass will consider them as fallback URIs to try # if the first URI does not work. # # It can be overriden via env using ${PN}_LIVE_REPO variable. # # Example: # @CODE # EGIT_REPO_URI="git://a/b.git https://c/d.git" # @CODE # @ECLASS-VARIABLE: EVCS_OFFLINE # @DEFAULT_UNSET # @DESCRIPTION: # If non-empty, this variable prevents any online operations. # @ECLASS-VARIABLE: EGIT_BRANCH # @DEFAULT_UNSET # @DESCRIPTION: # The branch name to check out. If unset, the upstream default (HEAD) # will be used. # # It can be overriden via env using ${PN}_LIVE_BRANCH variable. # @ECLASS-VARIABLE: EGIT_COMMIT # @DEFAULT_UNSET # @DESCRIPTION: # The tag name or commit identifier to check out. If unset, newest # commit from the branch will be used. If set, EGIT_BRANCH will # be ignored. # # It can be overriden via env using ${PN}_LIVE_COMMIT variable. # @ECLASS-VARIABLE: EGIT_CHECKOUT_DIR # @DESCRIPTION: # The directory to check the git sources out to. # # EGIT_CHECKOUT_DIR=${WORKDIR}/${P} # @ECLASS-VARIABLE: EGIT_NONSHALLOW # @DEFAULT_UNSET # @DESCRIPTION: # Disable performing shallow fetches/clones. Shallow clones have # a fair number of limitations. Therefore, if you'd like the eclass to # perform complete clones instead, set this to a non-null value. # # This variable is to be set in make.conf. Ebuilds are not allowed # to set it. # @FUNCTION: _git-r3_env_setup # @INTERNAL # @DESCRIPTION: # Set the eclass variables as necessary for operation. This can involve # setting EGIT_* to defaults or ${PN}_LIVE_* variables. _git-r3_env_setup() { debug-print-function ${FUNCNAME} "$@" local esc_pn livevar esc_pn=${PN//[-+]/_} livevar=${esc_pn}_LIVE_REPO EGIT_REPO_URI=${!livevar:-${EGIT_REPO_URI}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" livevar=${esc_pn}_LIVE_BRANCH EGIT_BRANCH=${!livevar:-${EGIT_BRANCH}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" livevar=${esc_pn}_LIVE_COMMIT EGIT_COMMIT=${!livevar:-${EGIT_COMMIT}} [[ ${!livevar} ]] \ && ewarn "Using ${livevar}, no support will be provided" # git-2 compat : ${EGIT_BRANCH:=${EGIT_MASTER}} : ${EGIT_CHECKOUT_DIR:=${EGIT_SOURCEDIR}} [[ ${EGIT_BOOTSTRAP} ]] && die "EGIT_BOOTSTRAP not supported." } # @FUNCTION: _git-r3_set_gitdir # @USAGE: <repo-uri> # @INTERNAL # @DESCRIPTION: # Obtain the local repository path and set it as GIT_DIR. Creates # a new repository if necessary. # # <repo-uri> may be used to compose the path. It should therefore be # a canonical URI to the repository. _git-r3_set_gitdir() { debug-print-function ${FUNCNAME} "$@" local repo_name=${1#*://*/} # strip common prefixes to make paths more likely to match # e.g. git://X/Y.git vs https://X/git/Y.git # (but just one of the prefixes) case "${repo_name}" in # cgit can proxy requests to git cgit/*) repo_name=${repo_name#cgit/};; # pretty common git/*) repo_name=${repo_name#git/};; # gentoo.org gitroot/*) repo_name=${repo_name#gitroot/};; # google code, sourceforge p/*) repo_name=${repo_name#p/};; # kernel.org pub/scm/*) repo_name=${repo_name#pub/scm/};; esac # ensure a .git suffix, same reason repo_name=${repo_name%.git}.git # now replace all the slashes repo_name=${repo_name//\//_} local distdir=${PORTAGE_ACTUAL_DISTDIR:-${DISTDIR}} : ${EGIT_STORE_DIR:=${distdir}/git3-src} GIT_DIR=${EGIT_STORE_DIR}/${repo_name} if [[ ! -d ${EGIT_STORE_DIR} ]]; then ( addwrite / mkdir -m0755 -p "${EGIT_STORE_DIR}" ) || die "Unable to create ${EGIT_STORE_DIR}" fi addwrite "${EGIT_STORE_DIR}" if [[ ! -d ${GIT_DIR} ]]; then mkdir "${GIT_DIR}" || die git init --bare || die # avoid auto-unshallow :) touch "${GIT_DIR}"/shallow || die fi } # @FUNCTION: _git-r3_set_submodules # @USAGE: <file-contents> # @INTERNAL # @DESCRIPTION: # Parse .gitmodules contents passed as <file-contents> # as in "$(cat .gitmodules)"). Composes a 'submodules' array that # contains in order (name, URL, path) for each submodule. _git-r3_set_submodules() { debug-print-function ${FUNCNAME} "$@" local data=${1} # ( name url path ... ) submodules=() local l while read l; do # submodule.<path>.path=<path> # submodule.<path>.url=<url> [[ ${l} == submodule.*.url=* ]] || continue l=${l#submodule.} local subname=${l%%.url=*} submodules+=( "${subname}" "$(echo "${data}" | git config -f /dev/fd/0 \ submodule."${subname}".url)" "$(echo "${data}" | git config -f /dev/fd/0 \ submodule."${subname}".path)" ) done < <(echo "${data}" | git config -f /dev/fd/0 -l) } # @FUNCTION: git-r3_fetch # @USAGE: [<repo-uri> [<remote-ref> [<local-id>]]] # @DESCRIPTION: # Fetch new commits to the local clone of repository. # # <repo-uri> specifies the repository URIs to fetch from, as a space- # -separated list. The first URI will be used as repository group # identifier and therefore must be used consistently. When not # specified, defaults to ${EGIT_REPO_URI}. # # <remote-ref> specifies the remote ref or commit id to fetch. # It is preferred to use 'refs/heads/<branch-name>' for branches # and 'refs/tags/<tag-name>' for tags. Other options are 'HEAD' # for upstream default branch and hexadecimal commit SHA1. Defaults # to the first of EGIT_COMMIT, EGIT_BRANCH or literal 'HEAD' that # is set to a non-null value. # # <local-id> specifies the local branch identifier that will be used to # locally store the fetch result. It should be unique to multiple # fetches within the repository that can be performed at the same time # (including parallel merges). It defaults to ${CATEGORY}/${PN}/${SLOT}. # This default should be fine unless you are fetching multiple trees # from the same repository in the same ebuild. # # The fetch operation will affect the EGIT_STORE only. It will not touch # the working copy, nor export any environment variables. # If the repository contains submodules, they will be fetched # recursively. git-r3_fetch() { debug-print-function ${FUNCNAME} "$@" local repos=( ${1:-${EGIT_REPO_URI}} ) local branch=${EGIT_BRANCH:+refs/heads/${EGIT_BRANCH}} local remote_ref=${2:-${EGIT_COMMIT:-${branch:-HEAD}}} local local_id=${3:-${CATEGORY}/${PN}/${SLOT}} local local_ref=refs/heads/${local_id}/__main__ [[ ${repos[@]} ]] || die "No URI provided and EGIT_REPO_URI unset" local -x GIT_DIR _git-r3_set_gitdir ${repos[0]} # try to fetch from the remote local r success for r in ${repos[@]}; do einfo "Fetching ${remote_ref} from ${r} ..." local is_branch lookup_ref if [[ ${remote_ref} == refs/heads/* || ${remote_ref} == HEAD ]] then is_branch=1 lookup_ref=${remote_ref} else # ls-remote by commit is going to fail anyway, # so we may as well pass refs/tags/ABCDEF... lookup_ref=refs/tags/${remote_ref} fi # first, try ls-remote to see if ${remote_ref} is a real ref # and not a commit id. if it succeeds, we can pass ${remote_ref} # to 'fetch'. otherwise, we will just fetch everything # split on whitespace local ref=( $(git ls-remote "${r}" "${lookup_ref}") ) # now, another important thing. we may only fetch a remote # branch directly to a local branch. Otherwise, we need to fetch # the commit and re-create the branch on top of it. local ref_param=() if [[ ! ${ref[0]} ]]; then local EGIT_NONSHALLOW=1 fi if [[ ! -f ${GIT_DIR}/shallow ]]; then # if it's a complete repo, fetch it as-is : elif [[ ${EGIT_NONSHALLOW} ]]; then # if it's a shallow clone but we need complete, # unshallow it ref_param+=( --unshallow ) else # otherwise, just fetch as shallow ref_param+=( --depth 1 ) fi if [[ ${ref[0]} ]]; then if [[ ${is_branch} ]]; then ref_param+=( -f "${remote_ref}:${local_id}/__main__" ) else ref_param+=( "refs/tags/${remote_ref}" ) fi fi # if ${remote_ref} is branch or tag, ${ref[@]} will contain # the respective commit id. otherwise, it will be an empty # array, so the following won't evaluate to a parameter. set -- git fetch --no-tags "${r}" "${ref_param[@]}" echo "${@}" >&2 if "${@}"; then if [[ ! ${is_branch} ]]; then set -- git branch -f "${local_id}/__main__" \ "${ref[0]:-${remote_ref}}" echo "${@}" >&2 if ! "${@}"; then die "Creating branch for ${remote_ref} failed (wrong ref?)." fi fi success=1 break fi done [[ ${success} ]] || die "Unable to fetch from any of EGIT_REPO_URI" # recursively fetch submodules if git cat-file -e "${local_ref}":.gitmodules &>/dev/null; then local submodules _git-r3_set_submodules \ "$(git cat-file -p "${local_ref}":.gitmodules || die)" while [[ ${submodules[@]} ]]; do local subname=${submodules[0]} local url=${submodules[1]} local path=${submodules[2]} local commit=$(git rev-parse "${local_ref}:${path}") if [[ ! ${commit} ]]; then die "Unable to get commit id for submodule ${subname}" fi git-r3_fetch "${url}" "${commit}" "${local_id}/${subname}" submodules=( "${submodules[@]:3}" ) # shift done fi } # @FUNCTION: git-r3_checkout # @USAGE: [<repo-uri> [<checkout-path> [<local-id>]]] # @DESCRIPTION: # Check the previously fetched tree to the working copy. # # <repo-uri> specifies the repository URIs, as a space-separated list. # The first URI will be used as repository group identifier # and therefore must be used consistently with git-r3_fetch. # The remaining URIs are not used and therefore may be omitted. # When not specified, defaults to ${EGIT_REPO_URI}. # # <checkout-path> specifies the path to place the checkout. It defaults # to ${EGIT_CHECKOUT_DIR} if set, otherwise to ${WORKDIR}/${P}. # # <local-id> needs to specify the local identifier that was used # for respective git-r3_fetch. # # The checkout operation will write to the working copy, and export # the repository state into the environment. If the repository contains # submodules, they will be checked out recursively. git-r3_checkout() { debug-print-function ${FUNCNAME} "$@" local repos=( ${1:-${EGIT_REPO_URI}} ) local out_dir=${2:-${EGIT_CHECKOUT_DIR:-${WORKDIR}/${P}}} local local_id=${3:-${CATEGORY}/${PN}/${SLOT}} local -x GIT_DIR GIT_WORK_TREE _git-r3_set_gitdir ${repos[0]} GIT_WORK_TREE=${out_dir} einfo "Checking out ${repos[0]} to ${out_dir} ..." mkdir -p "${GIT_WORK_TREE}" set -- git checkout -f "${local_id}"/__main__ . echo "${@}" >&2 "${@}" || die "git checkout ${local_id}/__main__ failed" # diff against previous revision (if any) local new_commit_id=$(git rev-parse --verify "${local_id}"/__main__) local old_commit_id=$( git rev-parse --verify "${local_id}"/__old__ 2>/dev/null ) if [[ ! ${old_commit_id} ]]; then echo "GIT NEW branch -->" echo " repository: ${repos[0]}" echo " at the commit: ${new_commit_id}" else echo "GIT update -->" echo " repository: ${repos[0]}" # write out message based on the revisions if [[ "${old_commit_id}" != "${new_commit_id}" ]]; then echo " updating from commit: ${old_commit_id}" echo " to commit: ${new_commit_id}" git --no-pager diff --color --stat \ ${old_commit_id}..${new_commit_id} else echo " at the commit: ${new_commit_id}" fi fi git branch -f "${local_id}"/{__old__,__main__} || die # recursively checkout submodules if [[ -f ${GIT_WORK_TREE}/.gitmodules ]]; then local submodules _git-r3_set_submodules \ "$(cat "${GIT_WORK_TREE}"/.gitmodules)" while [[ ${submodules[@]} ]]; do local subname=${submodules[0]} local url=${submodules[1]} local path=${submodules[2]} git-r3_checkout "${url}" "${GIT_WORK_TREE}/${path}" \ "${local_id}/${subname}" submodules=( "${submodules[@]:3}" ) # shift done fi # keep this *after* submodules export EGIT_DIR=${GIT_DIR} export EGIT_VERSION=${new_commit_id} } git-r3_src_fetch() { debug-print-function ${FUNCNAME} "$@" [[ ${EVCS_OFFLINE} ]] && return _git-r3_env_setup git-r3_fetch } git-2_src_unpack() { debug-print-function ${FUNCNAME} "$@" _git-r3_env_setup git-r3_src_fetch git-r3_checkout } _GIT_R3=1 fi
signature.asc
Description: PGP signature
