#!/bin/bash # # Copyright (c) 2020-2022 Wind River Systems, Inc. # # SPDX-License-Identifier: Apache-2.0 # # # Create a git branch from the current heads on a subset of git projects # within a manifest. A tag is also created to mark the commit where # the branch was forked from. The subset of projects affected can be # identified by project name or remote name. # # Optionally a new manifest (.xml) can be created that selects # the new branch for the affected projects. As a further option, # projects that are not branched can me 'locked down' within the new # manifest by setting the sha of the current head as the revision. # # See also: push_branches_tags.sh CREATE_BRANCHES_AND_TAGS_SH_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}" )" )" source "${CREATE_BRANCHES_AND_TAGS_SH_DIR}/../git-repo-utils.sh" usage () { echo "create_branches_and_tags.sh --branch= [--tag=] " echo "" echo " Create a branch on selected projects, and optionally update the manifest" echo " to use the the new branch." echo "" echo " The branch name must be provided. The tag name can also be provided." echo " If the tag is omitted, one is automativally generate by adding the" echo " prefix 'v' to the branch name." echo "" echo "selection options:" echo " [ --remotes= ] [ --projects= ]" echo "" echo " Create a branch and a tag in all listed projects, and all" echo " projects hosted by all listed remotes. Lists are comma separated." echo "" echo " [ --exclude-projects= ]" echo "" echo " Do not branch the list of projects. Used in conjunction with" echo " --remotes to branch everything from a remote minus a few projects." echo "" echo "gitreview options:" echo " Update any .gitreview files in branched projects." echo "" echo " [ --gitreview-host ]" echo " Set or update 'host' field." echo " [ --gitreview-port ]" echo " Set or update 'port' field." echo " [ --gitreview-project ]" echo " Set or update 'project' field." echo " [ --gitreview-default ]" echo " Set or update 'defaultbranch' field." echo " [ --safe-gerrit-host= ]" echo " allows one to specify host names of gerrit" echo " servers that are safe to push reviews to." echo "" echo "manifest options:" echo " [ --manifest ]" echo " Modify the current repo manifest to specify the" echo " new branch for all select remotes and projects." echo " [ --manifest-file= ]" echo " Override the manifest file to be updated." echo " [ --default-revision ]" echo " Set the default revision of the manifest." echo " [ --hard-lock-down | --lockdown ]" echo " All unselected projects get the current HEAD's" echo " SHA set as the revision." echo " [ --soft-lock-down ]" echo " All unselected projects with an revision that" echo " is unset, or 'master', get the current HEAD's sha" echo " set as the revision." echo " [ --lock-down-exclude-remotes= ]" echo " [ --lock-down-exclude-projects= ]" echo " Exclude from lock-down these additional projects, and" echo " all projects hosted by these additional remotes." echo " Lists are comma separated." echo "" } TEMP=$(getopt -o h --long remotes:,projects:,exclude-projects:,branch:,tag:,manifest,manifest-file:,lock-down,hard-lock-down,soft-lock-down,default-revision,gitreview-default,gitreview-project,gitreview-host:,gitreview-port:,safe-gerrit-host:,lock-down-exclude-remotes:,lock-down-exclude-projects:,help -n 'create_branches_and_tags.sh' -- "$@") if [ $? -ne 0 ]; then echo_stderr "ERROR: getopt failure" usage exit 1 fi eval set -- "$TEMP" HELP=0 MANIFEST=0 LOCK_DOWN=0 GITREVIEW_DEFAULT=0 GITREVIEW_PROJECT=0 GITREVIEW_HOST="" GITREVIEW_PORT="" GITREVIEW_CHANGE=0 SET_DEFAULT_REVISION=0 remotes="" projects="" exclude_projects="" ld_exclude_remotes="" ld_exclude_projects="" branch="" tag="" manifest="" new_manifest="" repo_root_dir="" safe_gerrit_hosts=() while true ; do case "$1" in -h|--help) HELP=1 ; shift ;; --remotes) remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; --projects) projects+=$(echo "$2 " | tr ',' ' '); shift 2;; --exclude-projects) exclude_projects+=$(echo "$2 " | tr ',' ' '); shift 2;; --lock-down-exclude-remotes) ld_exclude_remotes+=$(echo "$2 " | tr ',' ' '); shift 2;; --lock-down-exclude-projects) ld_exclude_projects+=$(echo "$2 " | tr ',' ' '); shift 2;; --branch) branch=$2; shift 2;; --tag) tag=$2; shift 2;; --manifest) MANIFEST=1 ; shift ;; --manifest-file) repo_set_manifest_file "$2" ; shift 2 ;; --lock-down) LOCK_DOWN=2 ; shift ;; --hard-lock-down) LOCK_DOWN=2 ; shift ;; --soft-lock-down) LOCK_DOWN=1 ; shift ;; --default-revision) SET_DEFAULT_REVISION=1 ; shift ;; --gitreview-default) GITREVIEW_DEFAULT=1 ; shift ;; --gitreview-project) GITREVIEW_PROJECT=1 ; shift ;; --gitreview-host) GITREVIEW_HOST=$2 ; shift 2;; --gitreview-port) GITREVIEW_PORT=$2 ; shift 2;; --safe-gerrit-host) safe_gerrit_hosts+=("$2") ; shift 2 ;; --) shift ; break ;; *) echo "unknown option $1"; usage; exit 1 ;; esac done git_set_safe_gerrit_hosts "${safe_gerrit_hosts[@]}" if [ $HELP -eq 1 ]; then usage exit 0 fi if [ "$branch" == "" ] ; then echo_stderr "ERROR: You must specify a branch" usage exit 1 fi repo_root_dir=$(repo_root) if [ $? -ne 0 ]; then echo_stderr "Current directory is not managed by repo." exit 1 fi if [ $GITREVIEW_DEFAULT -ne 0 ] || [ $GITREVIEW_PROJECT -ne 0 ] || [ "$GITREVIEW_HOST" != "" ] || [ "$GITREVIEW_PORT" != "" ]; then GITREVIEW_CHANGE=1 fi update_field () { local file=$1 local field=$2 local value=$3 local changed=0 if [ ! -f ${file} ]; then echo "File ${file} not found" return ${changed} fi if ! grep -q "^${field}=${value}$" ${file}; then echo "Updating ${field} in ${file}" if grep -q "^${field}=" ${file}; then sed "s#\(${field}=\).*#\1${value}#" -i ${file} else echo "${field}=${value}" >> ${file} fi changed=1 else echo "${field} in ${file} already set" fi return ${changed} } update_gitreview () { local DIR=$1 local review_method=$2 local need_rm=0 local need_commit=0 local message="Update .gitreview for ${branch}" local new_host=0 ( cd ${DIR} || exit 1 if [ ${GITREVIEW_CHANGE} -eq 1 ] && [ -f .gitreview ]; then if [ "${GITREVIEW_HOST}" != "" ]; then update_field ${PWD}/.gitreview host ${GITREVIEW_HOST} || need_commit=1 if [ ${need_commit} -ne 0 ]; then new_host=1 fi fi if [ "${GITREVIEW_PORT}" != "" ]; then update_field ${PWD}/.gitreview port ${GITREVIEW_PORT} || need_commit=1 fi if [ ${GITREVIEW_PROJECT} -eq 1 ]; then remote_url=$(git_repo_remote_url || git_remote_url) pull_url=${remote_url} path=$(url_path ${pull_url}) project=${path%.git} update_field ${PWD}/.gitreview project ${project} || need_commit=1 fi if [ ${GITREVIEW_DEFAULT} -eq 1 ]; then update_field ${PWD}/.gitreview defaultbranch ${branch} || need_commit=1 fi if [ $need_commit -eq 1 ]; then echo_stderr "determinging git review method in ${DIR}" [ -n "${review_method}" ] || review_method=$(git_repo_review_method) if [ "${review_method}" == "gerrit" ] ; then echo_stderr "running git review -s in ${DIR}" with_retries -d 45 -t 15 -k 5 5 git review -s >&2 if [ $? != 0 ] ; then if [ ${new_host} -eq 0 ]; then echo_stderr "ERROR: 3: failed to setup git review in ${DIR}" exit 1 fi need_rm=1 message="Delete .gitreview for ${branch}" fi echo_stderr "finished git review -s in ${DIR}" else need_rm=1 message="Delete .gitreview for ${branch}" fi if [ ${need_rm} -eq 1 ]; then git rm -f .gitreview if [ $? != 0 ] ; then echo_stderr "ERROR: failed to add .gitreview in ${DIR}" exit 1 fi else git add .gitreview if [ $? != 0 ] ; then echo_stderr "ERROR: failed to add .gitreview in ${DIR}" exit 1 fi fi git commit -s -m "${message}" if [ $? != 0 ] ; then echo_stderr "ERROR: failed to commit .gitreview in ${DIR}" exit 1 fi fi fi ) } if [ $MANIFEST -eq 1 ]; then manifest=$(repo_manifest $repo_root_dir) if [ $? -ne 0 ]; then echo_stderr "failed to find current manifest." exit 1 fi if [ ! -f $manifest ]; then echo_stderr "manifest file missing '$manifest'." exit 1 fi # String the leading feature (f/) or branch (b/) from the # branch name so we have a valid branch prefix if [[ ${branch} =~ .*/.*$ ]]; then manifest_prefix="$(basename ${branch})" else manifest_prefix="${branch}" fi new_manifest="$(dirname $manifest)/$manifest_prefix-$(basename $manifest)" if [ -f $new_manifest ]; then echo_stderr "branched manifest file already present '$new_manifest'." exit 1 fi fi for project in $projects $exclude_projects $ld_exclude_projects; do if ! repo_is_project $project; then echo_stderr "Invalid project: $project" echo_stderr "Valid projects are: $(repo_project_list | tr '\n' ' ')" exit 1 fi done for remote in $remotes $ld_exclude_remotes; do if ! repo_is_remote $remote; then echo_stderr "Invalid remote: $remote" echo_stderr "Valid remotes are: $(repo_remote_list | tr '\n' ' ')" exit 1 fi done # Add projects from listed remotes if [ "$remotes" != "" ]; then projects+="$(repo_project_list $remotes | tr '\n' ' ')" fi if [ "$ld_exclude_remotes" != "" ]; then ld_exclude_projects+="$(repo_project_list $ld_exclude_remotes | tr '\n' ' ')" fi # If no projects or remotes specified, process ALL projects if [ "$projects" == "" ] && [ "$remotes" == "" ]; then projects="$(repo_project_list)" fi if [ "$projects" != "" ] && [ "$exclude_projects" != "" ]; then for project in $exclude_projects; do projects=$(echo $projects | sed -e "s# $project # #" -e "s#^$project ##" -e "s# $project\$##" -e "s#^$project\$##") done fi if [ "$projects" == "" ]; then echo_stderr "No projects found" exit 1 fi echo "List of projects to be branched" echo "===============================" for project in $projects; do echo $project done echo "===============================" echo # Provide a default tag name if not otherwise provided if [ "$tag" == "" ]; then tag="v$branch" fi echo "Finding subgits" SUBGITS=$(repo forall $projects -c 'echo '"$repo_root_dir"'/$REPO_PATH') # Go through all subgits and create the branch and tag if it does not already exist echo "Applying branches and tags" ( for subgit in $SUBGITS; do ( cd $subgit review_method=$(git_repo_review_method) if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then with_retries -d 45 -t 15 -k 5 5 git review -s >&2 if [ $? != 0 ] ; then echo_stderr "ERROR: 1: failed to setup git review in ${subgit}" exit 1 fi fi remote=$(git_repo_remote) if [ "${remote}" == "" ]; then remote=$(git_remote) if [ "${remote}" == "" ]; then echo_stderr "ERROR: Failed to determine remote in ${subgit}" exit 1 fi fi remote_branch=$(git_repo_remote_branch) if [ "${remote_branch}" == "" ]; then remote_branch=$(git_remote_branch) fi extra_args="" if [ "${remote_branch}" != "" ]; then extra_args="-t ${remote}/${remote_branch}" fi # check if destination branch already exists branch_check=$(git branch -a --list $branch) if [ -z "$branch_check" ]; then echo "Creating branch $branch in $subgit" echo " git checkout ${extra_args} -b $branch" git checkout ${extra_args} -b $branch if [ $? != 0 ] ; then echo_stderr "ERROR: failed to create branch '$branch' in ${subgit}" exit 1 fi else echo "Branch $branch already exists in $subgit" git checkout $branch fi # check if destination tag already exists tag_check=$(git tag -l $tag) if [ -z "$tag_check" ]; then echo "Creating tag $tag in ${subgit}" git tag -s -m "Branch $branch" $tag if [ $? != 0 ] ; then echo "ERROR: failed to create tag '$tag' in ${subgit}" exit 1 fi else echo "Tag '$tag' already exists in ${subgit}" fi update_gitreview ${subgit} || exit 1 ) || exit 1 done ) || exit 1 if [ $MANIFEST -eq 1 ]; then ( echo "Starting manifest update" new_manifest_name=$(basename "${new_manifest}") new_manifest_dir=$(dirname "${new_manifest}") manifest_name=$(basename "${manifest}") manifest_dir=$(dirname "${manifest}") cd "${new_manifest_dir}" || exit 1 review_method=$(git_review_method) if [ -f .gitreview ] && [ "${review_method}" == "gerrit" ] ; then with_retries -d 45 -t 15 -k 5 5 git review -s >&2 if [ $? != 0 ] ; then echo_stderr "ERROR: 2: failed to setup git review in ${new_manifest_dir}" exit 1 fi fi branch_check=$(git branch -a --list $branch) if [ ! -z "$branch_check" ]; then echo "Branch $branch already exists in ${new_manifest_dir}" exit 1 fi tag_check=$(git tag -l $tag) if [ ! -z "$tag_check" ]; then echo "Tag '$tag' already exists in ${new_manifest_dir}" exit 1 fi remote=$(git_remote) if [ "${remote}" == "" ]; then echo_stderr "ERROR: Failed to determine remote in ${new_manifest_dir}" exit 1 fi remote_branch=$(git_remote_branch) if [ "${remote_branch}" == "" ]; then echo_stderr "ERROR: failed to determine remote branch in ${new_manifest_dir}" exit 1 fi echo "Creating branch '$branch' in ${new_manifest_dir}" git checkout -t ${remote}/${remote_branch} -b $branch if [ $? != 0 ] ; then echo_stderr "ERROR: failed to create branch '$branch' in ${new_manifest_dir}" exit 1 fi echo "Creating tag '$tag' in ${new_manifest_dir}" git tag -s -m "Branch $branch" $tag if [ $? != 0 ] ; then echo "ERROR: failed to create tag '$tag' in ${new_manifest_dir}" exit 1 fi update_gitreview ${manifest_dir} ${review_method} || exit 1 echo "Creating manifest ${new_manifest_name}" manifest_set_revision "${manifest}" "${new_manifest}" "$branch" "${LOCK_DOWN}" "${SET_DEFAULT_REVISION}" "${projects// /,}" "${ld_exclude_projects// /,}" || exit 1 echo "Move manifest ${new_manifest_name}, overwriting ${manifest_name}" \cp -f "${manifest}" "${manifest}.save" \mv -f "${new_manifest}" "${manifest}" echo "Committing ${manifest_name} in ${manifest_dir}" git add ${manifest_name} || exit 1 git commit -s -m "Modified manifest ${manifest_name} for branch ${branch}" if [ $? != 0 ] ; then echo_stderr "ERROR: failed to commit new manifest ${manifest_name} in ${manifest_dir}" exit 1 fi ) || exit 1 fi