root/build-tools/git-utils.sh
Scott Little d06b9aa763 Branch and tag tools converted from wrgit to repo
Update branching and tagging tools, used by patching, to do things
the 'repo' way.  Repo has replaced wrgit.

This is complicated by ...

1) Not all repos are under our control, and so can't be branched or
tagged by us.  The tools now accepts arguments that list 'remotes' or
'projects' to which branches or tags are to be applied

2) The manifest requires an update to reference the new branch or tag
as it applies to affected projects.  Non-affected projects can
optionally be locked down by converting the revision to a sha.
The tools must now try to generate that manifest.  It might be
sub-optimal.  i.e. a revision is explicitly added to each project,
rather than using the 'default' mechanism.  Perhaps something to address
in a future update.

ORIGINATING_BRANCH=master
ORIGINATING_BUILD=20200220T023000Z
NEW_BRANCH=r/stx.4.0
STX_REMOTES="starlingx"

repo init -u https://opendev.org/starlingx/manifest -b $ORIGINATING_BRANCH
repo sync --force-sync

curl http://mirror.starlingx.cengn.ca/mirror/starlingx/${ORIGINATING_BRANCH}/centos/${ORIGINATING_BUILD}/outputs/CONTEXT.sh > ${ORIGINATING_BUILD}.context

source ${ORIGINATING_BUILD}.context
create_branches_and_tags.sh --branch=$NEW_BRANCH --remotes=$STX_REMOTES --manifest --lockdown
push_branches_tags.sh --branch=$NEW_BRANCH --remotes=$STX_REMOTES --manifest

ORIGINATING_BRANCH=r/stx.4.0
ORIGINATING_BUILD=20200220T023000Z
NEW_STX_TAG=4.0.0
NEW_GIT_HUB_TAG=stx.4.0.0
STX_REMOTES="starlingx"
GIT_HUB_REMOTES="stx-staging"

repo init -u https://opendev.org/starlingx/manifest -b $ORIGINATING_BRANCH
repo sync --force-sync

curl http://mirror.starlingx.cengn.ca/mirror/starlingx/${ORIGINATING_BRANCH}/centos/${ORIGINATING_BUILD}/outputs/CONTEXT.sh > ${ORIGINATING_BUILD}.context

source ${ORIGINATING_BUILD}.context
TAG=TEST_19.10_PATCH_0000
create_tags.sh --tag=$NEW_STX_TAG --remotes=$STX_REMOTES,$GIT_HUB_REMOTES --manifest --manifest-prefix=stx. --lock-down
create_tags.sh --tag=$NEW_GIT_HUB_TAG --remotes=$GIT_HUB_REMOTES
push_tags.sh --tag=$NEW_STX_TAG --remotes=$STX_REMOTES
push_tags.sh --tag=$NEW_GIT_HUB_TAG --remotes=$STX_REMOTES
cd .repo/manifests
git mv $TAG-default.xml stx.$TAG.xml

vi stx.$TAG.xml
   # set revision for all stx-staging projects to refs/tags/stx.4.0.0
   <project remote="stx-staging" ... revision="refs/tags/stx.4.0.0" .

vi stx.$TAG.xml
   # set default revision
   <default remote="starlingx" revision="refs/tags/4.0.0"/>

   # delete 'revision' for all starlingx projects

git add stx.$TAG.xml
git commit -s --amend
    # set title to
    Locked down manifest for StarlingX $TAG

git tag -d $TAG
git review
git tag -s -m "Tag $TAG" $TAG
git push gerrit $TAG

Change-Id: I5ac0c3e44ffda262edb416e877d46c9036cd6e92
Signed-off-by: Scott Little <scott.little@windriver.com>
2020-04-03 17:02:38 -04:00

482 lines
10 KiB
Bash
Executable File

#!/bin/bash
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
#
# A place for any functions relating to git, or the git hierarchy created
# by repo manifests.
#
echo_stderr ()
{
echo "$@" >&2
}
git_ctx_root_dir () {
dirname "${MY_REPO}"
}
#
# git_list <dir>:
# Return a list of git root directories found under <dir>
#
git_list () {
local DIR=${1}
find -L "${DIR}" -maxdepth 5 -type d -name '.git' -exec dirname {} \; | sort -V
}
# GIT_LIST: A list of root directories for all the gits under $MY_REPO/..
# as absolute paths.
export GIT_LIST=$(git_list "$(git_ctx_root_dir)")
# GIT_LIST_REL: A list of root directories for all the gits under $MY_REPO/..
# as relative paths.
export GIT_LIST_REL=$(for p in $GIT_LIST; do echo .${p#$(git_ctx_root_dir)}; done)
#
# git_list_containing_branch <dir> <branch>:
# Return a list of git root directories found under <dir> and
# having branch <branch>. The branch need not be current branch.
#
git_list_containing_branch () {
local DIR="${1}"
local BRANCH="${2}"
local d
for d in $(git_list "${DIR}"); do
(
cd "$d"
git branch --all | grep -q "$BRANCH"
if [ $? -eq 0 ]; then
echo "$d"
fi
)
done
}
#
# git_list_containing_tag <dir> <tag>:
# Return a list of git root directories found under <dir> and
# having tag <tag>.
#
git_list_containing_tag () {
local DIR="${1}"
local TAG="${2}"
local d
for d in $(git_list "${DIR}"); do
(
cd "$d"
git tag | grep -q "$TAG"
if [ $? -eq 0 ]; then
echo "$d"
fi
)
done
}
#
# git_root [<dir>]:
# Return the root directory of a git
# Note: symlinks are fully expanded.
#
git_root () {
local DIR="${1:-${PWD}}"
if [ ! -d "${DIR}" ]; then
echo_stderr "No such directory: ${DIR}"
return 1
fi
(
cd "${DIR}"
ROOT_DIR="$(git rev-parse --show-toplevel)" || exit 1
readlink -f "${ROOT_DIR}"
)
}
#
# git_list_tags [<dir>]:
# Return a list of all git tags.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_list_tags () {
local DIR="${1:-${PWD}}"
(
cd "$DIR"
git tag
)
}
#
# git_list_branches [<dir>]:
# Return a list of all git branches.
# Non-local branches will be prefixed by 'remote/<remote-name>'
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_list_branches () {
local DIR="${1:-${PWD}}"
(
cd "$DIR"
git branch --list --all | sed 's#^..##'
)
}
#
# git_list_remote_branches <remote> [<dir>]:
# Return a list of all git branches defined for <remote>.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_list_remote_branches () {
local REMOTE="${1}"
local DIR="${2:-${PWD}}"
(
cd "$DIR"
git branch --list --all "${REMOTE}/*" | sed "s#^.*/${REMOTE}/##"
)
}
#
# git_is_tag <tag> [<dir>]:
# Test if a <tag> is defined within a git.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_tag () {
local TAG="${1}"
local DIR="${2:-${PWD}}"
# remove a trailing ^0 if present
TAG=${TAG%^0}
if [ "$TAG" == "" ]; then
return 1;
fi
(
cd "$DIR"
git show-ref ${TAG} | cut -d ' ' -f 2 | grep -q '^refs/tags/'
)
}
#
# git_is_local_branch <branch> [<dir>]:
# Test if a <branch> is defined locally within a git.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_local_branch () {
local BRANCH="${1}"
local DIR="${2:-${PWD}}"
if [ "$BRANCH" == "" ]; then
return 1;
fi
(
cd "$DIR"
git show-ref ${BRANCH} | cut -d ' ' -f 2 | grep -q '^refs/heads/'
)
}
#
# git_is_remote_branch <branch> [<dir>]:
# Test if a <branch> is defined in any of the remotes of the git.
# The branche does NOT need to be prefixed by the remore name.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_remote_branch () {
local BRANCH="${1}"
local DIR="${2:-${PWD}}"
if [ "$BRANCH" == "" ]; then
return 1;
fi
(
cd "$DIR"
git show-ref ${BRANCH} | cut -d ' ' -f 2 | grep -q '^refs/remotes/'
)
}
#
# git_is_branch <branch> [<dir>]:
# Test if a <branch> is defined in the git.
# The branch can be local or remote.
# Remote branches do NOT need to be prefixed by the remore name.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_branch () {
local BRANCH="${1}"
local DIR="${2:-${PWD}}"
if [ "$BRANCH" == "" ]; then
return 1;
fi
git_is_local_branch ${BRANCH} "${DIR}" || git_is_remote_branch ${BRANCH} "${DIR}"
}
#
# git_is_ref <ref> [<dir>]:
# Test if a <ref> is a valid name for a commit.
# The reference can be a sha, tag, or branch.
# Remote branches must be prefixed by the remore name,
# as in <remote-name>/<branch> .
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_ref () {
local REF="${1}"
local DIR="${2:-${PWD}}"
if [ "$REF" == "" ]; then
return 1;
fi
# test "$(git cat-file -t ${REF})" == "commit"
local TYPE=""
TYPE="$(git cat-file -t ${REF} 2> /dev/null)" && test "${TYPE}" == "commit"
}
#
# git_is_sha <sha> [<dir>]:
# Test if a <sha> is defined in the git. The sha can be abreviated.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
#
git_is_sha () {
local SHA="${1}"
local DIR="${2:-${PWD}}"
if [ "$SHA" == "" ]; then
return 1;
fi
git_is_ref ${SHA} "${DIR}" && ! ( git_is_branch ${SHA} "${DIR}" || git_is_tag ${SHA} "${DIR}")
}
#
# git_ref_type <ref> [<dir>]:
# Determine the type of the git reference <ref>.
# The result, via stdout, will be one of ("sha", "tag", "branch" or "invalid")
# Remote branches do NOT need to be prefixed by the remore name.
# Either specify a directory <dir> in the git,
# or use current directory if unspecified.
git_ref_type () {
local REF="${1}"
local DIR="${2:-${PWD}}"
if git_is_branch ${REF} ${DIR}; then
echo 'branch'
return 0
fi
if git_is_tag ${REF} ${DIR}; then
echo 'tag'
return 0
fi
if git_is_sha ${REF} ${DIR}; then
echo 'sha'
return 0
fi
echo 'invalid'
return 1
}
#
#
# git_context:
# Returns a bash script that can be used to recreate the current git context,
#
# Note: all paths are relative to $MY_REPO/..
#
git_context () {
(
cd $(git_ctx_root_dir)
local d
for d in $GIT_LIST_REL; do
(
cd ${d}
echo -n "(cd ${d} && git checkout -f "
echo "$(git rev-list HEAD -1))"
)
done
)
}
#
# git_test_context <context>:
#
# Test if all commits referenced in the context are present
# in the history of the gits in their current checkout state.
#
# Returns: 0 = context is present in git history
# 1 = At least one element of context is not present
# 2 = error
#
git_test_context () {
local context="$1"
local query=""
local target_hits=0
local actual_hits=0
if [ ! -f "$context" ]; then
return 2
fi
query=$(mktemp "/tmp/git_test_context_XXXXXX")
if [ "$query" == "" ]; then
return 2
fi
# Transform a checkout context into a query that prints
# all the commits that are found in the git history.
#
# Limit search to last 500 commits in the interest of speed.
# I don't expect to be using contexts more than a few weeks old.
cat "$context" | \
sed "s#checkout -f \([a-e0-9]*\)#rev-list --max-count=500 HEAD | \
grep \1#" > $query
target_hits=$(cat "$context" | wc -l)
actual_hits=$(cd $(git_ctx_root_dir); source $query | wc -l)
\rm $query
if [ $actual_hits -eq $target_hits ]; then
return 0
fi
return 1
}
git_local_branch () {
local result=""
result=$(git name-rev --name-only HEAD)
if [ "$result" == "" ] || [ "$result" == "undefined" ]; then
return 1
fi
# handle the case where a tag is returned by looking at the parent.
# This weird case when a local commit is tagged and we were in
# detached head state, or on 'default' branch.
while git_is_tag $result; do
result=$(git name-rev --name-only $result^1 )
if [ "$result" == "" ] || [ "$result" == "undefined" ]; then
return 1
fi
done
echo $result
}
git_list_remotes () {
git remote | grep -v gerrit
}
git_remote () {
local DIR="${1:-${PWD}}"
(
cd ${DIR}
local_branch=$(git_local_branch) || return 1
# Return remote of current local branch, else default remote.
git config branch.${local_branch}.remote || git_list_remotes
)
}
git_remote_url () {
local remote=""
remote=$(git_remote) || return 1
git config remote.$remote.url
}
git_remote_branch () {
local local_branch=""
local_branch=$(git_local_branch) || return 1
git config branch.${local_branch}.merge | sed 's#^refs/heads/##'
}
git_review_method () {
local url=""
url=$(git_remote_url) || exit 1
if [[ "${url}" =~ "/git.starlingx.io/" || "${url}" =~ "/opendev.org/" ]]; then
echo 'gerrit'
else
echo 'default'
fi
}
git_review_url () {
local method=""
method=$(git_review_method)
if [ "${method}" == "gerrit" ]; then
git config remote.gerrit.url
if [ $? -ne 0 ]; then
# Perhaps we need to run git review -s' and try again
git review -s > /dev/null || return 1
git config remote.gerrit.url
fi
else
git_remote_url
fi
}
git_review_remote () {
local method=""
method=$(git_review_method)
if [ "${method}" == "gerrit" ]; then
git config remote.gerrit.url > /dev/null
if [ $? -ne 0 ]; then
# Perhaps we need to run git review -s' and try again
git review -s > /dev/null || return 1
git config remote.gerrit.url > /dev/null || return 1
fi
echo "gerrit"
else
git_remote
fi
}