# Copyright 1999-2009 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # $Header: /var/cvsroot/gentoo-x86/eclass/git.eclass,v 1.44 2010/03/17 15:17:10 sping Exp $ # @ECLASS: git.eclass # @MAINTAINER: # Tomas Chvatal <scarabeus@gentoo.org> # Donnie Berkholz <dberkholz@gentoo.org> # @BLURB: This eclass provides functions for fetch and unpack git repositories # @DESCRIPTION: # The eclass is based on subversion eclass. # If you use this eclass, the ${S} is ${WORKDIR}/${P}. # It is necessary to define the EGIT_REPO_URI variable at least. # @THANKS TO: # Fernando J. Pereda <ferdy@gentoo.org> inherit eutils EGIT="git.eclass" # We DEPEND on at least a bit recent git version DEPEND=">=dev-vcs/git-1.6" EXPORTED_FUNCTIONS="src_unpack" case "${EAPI:-0}" in 3|2) EXPORTED_FUNCTIONS="${EXPORTED_FUNCTIONS} src_prepare" ;; 1|0) ;; :) DEPEND="EAPI-UNSUPPORTED" ;; esac EXPORT_FUNCTIONS ${EXPORTED_FUNCTIONS} # define some nice defaults but only if nothing is set already : ${HOMEPAGE:=http://git-scm.com/} # @ECLASS-VARIABLE: EGIT_QUIET # @DESCRIPTION: # Enables user specified verbosity for the eclass elog informations. # The user just needs to add EGIT_QUIET="ON" to the /etc/make.conf. : ${EGIT_QUIET:="OFF"} # @ECLASS-VARIABLE: EGIT_STORE_DIR # @DESCRIPTION: # Storage directory for git sources. # Can be redefined. [[ -z ${EGIT_STORE_DIR} ]] && EGIT_STORE_DIR="${PORTAGE_ACTUAL_DISTDIR-${DISTDIR}}/git-src" # @ECLASS-VARIABLE: EGIT_HAS_SUBMODULES # @DESCRIPTION: # Set this to "true" to enable the (slower) submodule support. # This variable should be set before inheriting git.eclass : ${EGIT_HAS_SUBMODULES:=false} # @ECLASS-VARIABLE: EGIT_FETCH_CMD # @DESCRIPTION: # Command for cloning the repository. : ${EGIT_FETCH_CMD:="git clone"} # @ECLASS-VARIABLE: EGIT_UPDATE_CMD # @DESCRIPTION: # Git fetch command. if ${EGIT_HAS_SUBMODULES}; then EGIT_UPDATE_CMD="git pull -f -u" else EGIT_UPDATE_CMD="git fetch -f -u" fi # @ECLASS-VARIABLE: EGIT_DIFFSTAT_CMD # @DESCRIPTION: # Git command for diffstat. EGIT_DIFFSTAT_CMD="git --no-pager diff --stat" # @ECLASS-VARIABLE: EGIT_OPTIONS # @DESCRIPTION: # This variable value is passed to clone and fetch. : ${EGIT_OPTIONS:=} # @ECLASS-VARIABLE: EGIT_MASTER # @DESCRIPTION: # Variable for specifying master branch. # Usefull when upstream don't have master branch. : ${EGIT_MASTER:=master} # @ECLASS-VARIABLE: EGIT_REPO_URI # @DESCRIPTION: # URI for the repository # e.g. http://foo, git://bar # Supported protocols: # http:// # https:// # git:// # git+ssh:// # rsync:// # ssh:// eval X="\$${PN//[-+]/_}_LIVE_REPO" if [[ ${X} = "" ]]; then EGIT_REPO_URI=${EGIT_REPO_URI:=} else EGIT_REPO_URI="${X}" fi # @ECLASS-VARIABLE: EGIT_PROJECT # @DESCRIPTION: # Project name of your ebuild. # Git eclass will check out the git repository like: # ${EGIT_STORE_DIR}/${EGIT_PROJECT}/${EGIT_REPO_URI##*/} # so if you define EGIT_REPO_URI as http://git.collab.net/repo/git or # http://git.collab.net/repo/git. and PN is subversion-git. # it will check out like: # ${EGIT_STORE_DIR}/subversion : ${EGIT_PROJECT:=${PN/-git}} # @ECLASS-VARIABLE: EGIT_BOOTSTRAP # @DESCRIPTION: # bootstrap script or command like autogen.sh or etc... : ${EGIT_BOOTSTRAP:=} # @ECLASS-VARIABLE: EGIT_OFFLINE # @DESCRIPTION: # Set this variable to a non-empty value to disable the automatic updating of # an GIT source tree. This is intended to be set outside the git source # tree by users. EGIT_OFFLINE="${EGIT_OFFLINE:-${ESCM_OFFLINE}}" # @ECLASS-VARIABLE: EGIT_PATCHES # @DESCRIPTION: # Similar to PATCHES array from base.eclass # Only difference is that this patches are applied before bootstrap. # Please take note that this variable should be bash array. # @ECLASS-VARIABLE: EGIT_BRANCH # @DESCRIPTION: # git eclass can fetch any branch in git_fetch(). eval X="\$${PN//[-+]/_}_LIVE_BRANCH" if [[ ${X} = "" ]]; then EGIT_BRANCH=${EGIT_BRANCH:=master} else EGIT_BRANCH="${X}" fi # @ECLASS-VARIABLE: EGIT_COMMIT # @DESCRIPTION: # git eclass can checkout any commit. eval X="\$${PN//[-+]/_}_LIVE_COMMIT" if [[ ${X} = "" ]]; then : ${EGIT_COMMIT:=${EGIT_BRANCH}} else EGIT_COMMIT="${X}" fi # @ECLASS-VARIABLE: EGIT_REPACK # @DESCRIPTION: # git eclass will repack objects to save disk space. However this can take a # long time with VERY big repositories. : ${EGIT_REPACK:=false} # @ECLASS-VARIABLE: EGIT_PRUNE # @DESCRIPTION: # git eclass can prune the local clone. This is useful if upstream rewinds and # rebases branches too often. : ${EGIT_PRUNE:=false} # @FUNCTION: git_submodules # @DESCRIPTION: # Internal function wrapping the submodule initialisation and update git_submodules() { if ${EGIT_HAS_SUBMODULES}; then debug-print "git submodule init" git submodule init debug-print "git submodule update" git submodule update fi } # @FUNCTION: git_branch # @DESCRIPTION: # Internal function that changes branch for the repo based on EGIT_TREE and # EGIT_BRANCH variables. git_branch() { local branchname=branch-${EGIT_BRANCH} src=origin/${EGIT_BRANCH} if [[ ${EGIT_COMMIT} != ${EGIT_BRANCH} ]]; then branchname=tree-${EGIT_COMMIT} src=${EGIT_COMMIT} fi debug-print "git checkout -b ${branchname} ${src}" git checkout -b ${branchname} ${src} &> /dev/null unset branchname src } # @FUNCTION: git_fetch # @DESCRIPTION: # Gets repository from EGIT_REPO_URI and store it in specified EGIT_STORE_DIR git_fetch() { debug-print-function ${FUNCNAME} "$@" local GIT_DIR EGIT_CLONE_DIR oldsha1 cursha1 extra_clone_opts upstream_branch ${EGIT_HAS_SUBMODULES} || export GIT_DIR # choose if user wants elog or just einfo. if [[ ${EGIT_QUIET} != OFF ]]; then elogcmd="einfo" else elogcmd="elog" fi # If we have same branch and the tree we can do --depth 1 clone # which outputs into really smaller data transfers. # Sadly we can do shallow copy for now because quite a few packages need .git # folder. #[[ ${EGIT_COMMIT} = ${EGIT_BRANCH} ]] && \ # EGIT_FETCH_CMD="${EGIT_FETCH_CMD} --depth 1" if [[ ! -z ${EGIT_TREE} ]] ; then EGIT_COMMIT=${EGIT_TREE} ewarn "QA: Usage of deprecated EGIT_TREE variable detected." ewarn "QA: Use EGIT_COMMIT variable instead." fi # EGIT_REPO_URI is empty. [[ -z ${EGIT_REPO_URI} ]] && die "${EGIT}: EGIT_REPO_URI is empty." # check for the protocol or pull from a local repo. if [[ -z ${EGIT_REPO_URI%%:*} ]] ; then case ${EGIT_REPO_URI%%:*} in git*|http|https|rsync|ssh) ;; *) die "${EGIT}: protocol for fetch from "${EGIT_REPO_URI%:*}" is not yet implemented in eclass." ;; esac fi # initial clone, we have to create master git storage directory and play # nicely with sandbox if [[ ! -d ${EGIT_STORE_DIR} ]] ; then debug-print "${FUNCNAME}: initial clone. creating git directory" addwrite / mkdir -p "${EGIT_STORE_DIR}" \ || die "${EGIT}: can't mkdir ${EGIT_STORE_DIR}." export SANDBOX_WRITE="${SANDBOX_WRITE%%:/}" fi cd -P "${EGIT_STORE_DIR}" || die "${EGIT}: can't chdir to ${EGIT_STORE_DIR}" EGIT_STORE_DIR=${PWD} # allow writing into EGIT_STORE_DIR addwrite "${EGIT_STORE_DIR}" [[ -z ${EGIT_REPO_URI##*/} ]] && EGIT_REPO_URI="${EGIT_REPO_URI%/}" EGIT_CLONE_DIR="${EGIT_PROJECT}" debug-print "${FUNCNAME}: EGIT_OPTIONS = \"${EGIT_OPTIONS}\"" GIT_DIR="${EGIT_STORE_DIR}/${EGIT_CLONE_DIR}" # we also have to remove all shallow copied repositories # and fetch them again if [[ -e "${GIT_DIR}/shallow" ]]; then rm -rf "${GIT_DIR}" einfo "The ${EGIT_CLONE_DIR} was shallow copy. Refetching." fi # repack from bare copy to normal one if ${EGIT_HAS_SUBMODULES} && [[ -d ${GIT_DIR} && ! -d "${GIT_DIR}/.git/" ]]; then rm -rf "${GIT_DIR}" einfo "The ${EGIT_CLONE_DIR} was bare copy. Refetching." fi if ! ${EGIT_HAS_SUBMODULES} && [[ -d ${GIT_DIR} && -d ${GIT_DIR}/.git ]]; then rm -rf "${GIT_DIR}" einfo "The ${EGIT_CLONE_DIR} was not a bare copy. Refetching." fi if ${EGIT_HAS_SUBMODULES}; then upstream_branch=origin/${EGIT_BRANCH} else upstream_branch=${EGIT_BRANCH} extra_clone_opts=--bare fi if [[ ! -d ${GIT_DIR} ]] ; then # first clone ${elogcmd} "GIT NEW clone -->" ${elogcmd} " repository: ${EGIT_REPO_URI}" debug-print "${EGIT_FETCH_CMD} ${extra_clone_opts} ${EGIT_OPTIONS} \"${EGIT_REPO_URI}\" ${GIT_DIR}" ${EGIT_FETCH_CMD} ${extra_clone_opts} ${EGIT_OPTIONS} "${EGIT_REPO_URI}" ${GIT_DIR} \ || die "${EGIT}: can't fetch from ${EGIT_REPO_URI}." pushd "${GIT_DIR}" &> /dev/null cursha1=$(git rev-parse ${upstream_branch}) ${elogcmd} " at the commit: ${cursha1}" git_submodules popd &> /dev/null elif [[ -n ${EGIT_OFFLINE} ]] ; then pushd "${GIT_DIR}" &> /dev/null cursha1=$(git rev-parse ${upstream_branch}) ${elogcmd} "GIT offline update -->" ${elogcmd} " repository: ${EGIT_REPO_URI}" ${elogcmd} " at the commit: ${cursha1}" popd &> /dev/null else pushd "${GIT_DIR}" &> /dev/null # Git urls might change, so unconditionally set it here git config remote.origin.url "${EGIT_REPO_URI}" # fetch updates ${elogcmd} "GIT update -->" ${elogcmd} " repository: ${EGIT_REPO_URI}" oldsha1=$(git rev-parse ${upstream_branch}) if ${EGIT_HAS_SUBMODULES}; then debug-print "${EGIT_UPDATE_CMD} ${EGIT_OPTIONS}" # fix branching git checkout ${EGIT_MASTER} for x in $(git branch |grep -v "* ${EGIT_MASTER}" |tr '\n' ' '); do git branch -D ${x} done ${EGIT_UPDATE_CMD} ${EGIT_OPTIONS} \ || die "${EGIT}: can't update from ${EGIT_REPO_URI}." else debug-print "${EGIT_UPDATE_CMD} ${EGIT_OPTIONS} origin ${EGIT_BRANCH}:${EGIT_BRANCH}" ${EGIT_UPDATE_CMD} ${EGIT_OPTIONS} origin ${EGIT_BRANCH}:${EGIT_BRANCH} \ || die "${EGIT}: can't update from ${EGIT_REPO_URI}." fi git_submodules cursha1=$(git rev-parse ${upstream_branch}) # write out message based on the revisions if [[ ${oldsha1} != ${cursha1} ]]; then ${elogcmd} " updating from commit: ${oldsha1}" ${elogcmd} " to commit: ${cursha1}" else ${elogcmd} " at the commit: ${cursha1}" # @ECLASS_VARIABLE: LIVE_FAIL_FETCH_IF_REPO_NOT_UPDATED # @DESCRIPTION: # If this variable is set to TRUE in make.conf or somewhere in # enviroment the package will fail if there is no update, thus in # combination with --keep-going it would lead in not-updating # pakcages that are up-to-date. # TODO: this can lead to issues if more projects/packages use same repo [[ ${LIVE_FAIL_FETCH_IF_REPO_NOT_UPDATED} = true ]] && \ debug-print "${FUNCNAME}: Repository \"${EGIT_REPO_URI}\" is up-to-date. Skipping." && \ die "${EGIT}: Repository \"${EGIT_REPO_URI}\" is up-to-date. Skipping." fi ${EGIT_DIFFSTAT_CMD} ${oldsha1}..${upstream_branch} popd &> /dev/null fi pushd "${GIT_DIR}" &> /dev/null if ${EGIT_REPACK} || ${EGIT_PRUNE} ; then ebegin "Garbage collecting the repository" git gc $(${EGIT_PRUNE} && echo '--prune') eend $? fi popd &> /dev/null # export the git version export EGIT_VERSION="${cursha1}" # log the repo state [[ ${EGIT_COMMIT} != ${EGIT_BRANCH} ]] && elog " commit: ${EGIT_COMMIT}" ${elogcmd} " branch: ${EGIT_BRANCH}" ${elogcmd} " storage directory: \"${GIT_DIR}\"" if ${EGIT_HAS_SUBMODULES}; then pushd "${GIT_DIR}" &> /dev/null debug-print "rsync -rlpgo . \"${S}\"" time rsync -rlpgo . "${S}" popd &> /dev/null else unset GIT_DIR debug-print "git clone -l -s -n \"${EGIT_STORE_DIR}/${EGIT_CLONE_DIR}\" \"${S}\"" git clone -l -s -n "${EGIT_STORE_DIR}/${EGIT_CLONE_DIR}" "${S}" fi pushd "${S}" &> /dev/null git_branch # submodules always reqire net (thanks to branches changing) [[ -n ${EGIT_OFFLINE} ]] || git_submodules popd &> /dev/null echo ">>> Unpacked to ${S}" } # @FUNCTION: git_bootstrap # @DESCRIPTION: # Runs bootstrap command if EGIT_BOOTSTRAP variable contains some value # Remember that what ever gets to the EGIT_BOOTSTRAP variable gets evaled by bash. git_bootstrap() { debug-print-function ${FUNCNAME} "$@" if [[ -n ${EGIT_BOOTSTRAP} ]] ; then pushd "${S}" > /dev/null einfo "Starting bootstrap" if [[ -f ${EGIT_BOOTSTRAP} ]]; then # we have file in the repo which we should execute debug-print "$FUNCNAME: bootstraping with file \"${EGIT_BOOTSTRAP}\"" if [[ -x ${EGIT_BOOTSTRAP} ]]; then eval "./${EGIT_BOOTSTRAP}" \ || die "${EGIT}: bootstrap script failed" else eerror "\"${EGIT_BOOTSTRAP}\" is not executable." eerror "Report upstream, or bug ebuild maintainer to remove bootstrap command." die "${EGIT}: \"${EGIT_BOOTSTRAP}\" is not executable." fi else # we execute some system command debug-print "$FUNCNAME: bootstraping with commands \"${EGIT_BOOTSTRAP}\"" eval "${EGIT_BOOTSTRAP}" \ || die "${EGIT}: bootstrap commands failed." fi einfo "Bootstrap finished" popd > /dev/null fi } # @FUNCTION: git_apply_patches # @DESCRIPTION: # Apply patches from EGIT_PATCHES bash array. # Preffered is using the variable as bash array but for now it allows to write # it also as normal space separated string list. (This part of code should be # removed when all ebuilds get converted on bash array). git_apply_patches() { debug-print-function ${FUNCNAME} "$@" pushd "${S}" > /dev/null if [[ ${#EGIT_PATCHES[@]} -gt 1 ]] ; then for i in "${EGIT_PATCHES[@]}"; do debug-print "$FUNCNAME: git_autopatch: patching from ${i}" epatch "${i}" done elif [[ ${EGIT_PATCHES} != "" ]]; then # no need for loop if space separated string is passed. debug-print "$FUNCNAME: git_autopatch: patching from ${EGIT_PATCHES}" epatch "${EGIT_PATCHES}" fi popd > /dev/null } # @FUNCTION: git_src_unpack # @DESCRIPTION: # src_upack function, calls src_prepare one if EAPI!=2. git_src_unpack() { debug-print-function ${FUNCNAME} "$@" git_fetch || die "${EGIT}: unknown problem in git_fetch()." has src_prepare ${EXPORTED_FUNCTIONS} || git_src_prepare } # @FUNCTION: git_src_prepare # @DESCRIPTION: # src_prepare function for git stuff. Patches, bootstrap... git_src_prepare() { debug-print-function ${FUNCNAME} "$@" git_apply_patches git_bootstrap }