Path: blob/master/Utilities/Scripts/update-third-party.bash
3148 views
#=============================================================================1# Copyright 2015-2016 Kitware, Inc.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14#=============================================================================1516########################################################################17# Script for updating third party packages.18#19# This script should be sourced in a project-specific script which sets20# the following variables:21#22# name23# The name of the project.24# ownership25# A git author name/email for the commits.26# subtree27# The location of the third-party package within the main source28# tree.29# repo30# The git repository to use as upstream.31# tag32# The tag, branch or commit hash to use for upstream.33# shortlog34# Optional. Set to 'true' to get a shortlog in the commit message.35#36# Additionally, an "extract_source" function must be defined. It will be37# run within the checkout of the project on the requested tag. It should38# should place the desired tree into $extractdir/$name-reduced. This39# directory will be used as the newest commit for the project.40#41# For convenience, the function may use the "git_archive" function which42# does a standard "git archive" extraction using the (optional) "paths"43# variable to only extract a subset of the source tree.44#45# Dependencies46#47# To update third party packages from git repositories with submodule,48# you will need to install the "git-archive-all" Python package with49#50# pip install git-archive-all51#52# or install it from https://github.com/Kentzo/git-archive-all.53#54# This package installs a script named "git-archive-all" where pip55# installs executables. If you run pip under your user privileges (i.e.,56# not using "sudo"), this location may be $HOME/.local/bin. Make sure57# that directory is in your path so that git can find the58# "git-archive-all" script.59#60########################################################################6162########################################################################63# Utility functions64########################################################################65git_archive () {66git archive --worktree-attributes --prefix="$name-reduced/" HEAD -- $paths | \67tar -C "$extractdir" -x68}6970confirm_archive_all_exists () {71which git-archive-all || die "git requires an archive-all command. Please run 'pip install git-archive-all'"72}7374git_archive_all () {75confirm_archive_all_exists76local tmptarball="temp.tar"77git archive-all --prefix="" "$tmptarball"78mkdir -p "$extractdir/$name-reduced"79tar -C "$extractdir/$name-reduced" -xf "$tmptarball" $paths80rm -f "$tmptarball"81}8283disable_custom_gitattributes() {84pushd "${extractdir}/${name}-reduced"85# Git does not allow custom attributes in a subdirectory where we86# are about to merge the `.gitattributes` file, so disable them.87sed -i '/^\[attr\]/ {s/^/#/;}' .gitattributes88popd89}9091die () {92echo >&2 "$@"93exit 194}9596warn () {97echo >&2 "warning: $@"98}99100readonly regex_date='20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]'101readonly basehash_regex="$name $regex_date ([0-9a-f]*)"102readonly toplevel_dir="$( git rev-parse --show-toplevel )"103104cd "$toplevel_dir"105106########################################################################107# Sanity checking108########################################################################109[ -n "$name" ] || \110die "'name' is empty"111[ -n "$ownership" ] || \112die "'ownership' is empty"113[ -n "$subtree" ] || \114die "'subtree' is empty"115[ -n "$repo" ] || \116die "'repo' is empty"117[ -n "$tag" ] || \118die "'tag' is empty"119120# Check for an empty destination directory on disk. By checking on disk and121# not in the repo it allows a library to be freshly re-initialized in a single122# commit rather than first deleting the old copy in one commit and adding the123# new copy in a separate commit.124if [ ! -d "$(git rev-parse --show-toplevel)/$subtree" ]; then125readonly basehash=""126else127readonly basehash="$( git rev-list --author="$ownership" --grep="$basehash_regex" -n 1 HEAD )"128fi129readonly upstream_old_short="$( git cat-file commit "$basehash" | sed -n '/'"$basehash_regex"'/ {s/.*(//;s/)//;p;}' | egrep '^[0-9a-f]+$' )"130131[ -n "$basehash" ] || \132warn "'basehash' is empty; performing initial import"133readonly do_shortlog="${shortlog-false}"134135readonly workdir="$PWD/work"136readonly upstreamdir="$workdir/upstream"137readonly extractdir="$workdir/extract"138139[ -d "$workdir" ] && \140die "error: workdir '$workdir' already exists"141142trap "rm -rf '$workdir'" EXIT143144# Get upstream145git clone --recursive "$repo" "$upstreamdir"146147if [ -n "$basehash" ]; then148# Remove old worktrees149git worktree prune150# Use the existing package's history151git worktree add "$extractdir" "$basehash"152# Clear out the working tree153pushd "$extractdir"154git ls-files -z --recurse-submodules | xargs -0 rm -v155find . -type d -empty -delete156popd157else158# Create a repo to hold this package's history159mkdir -p "$extractdir"160git -C "$extractdir" init161fi162163# Extract the subset of upstream we care about164pushd "$upstreamdir"165git checkout "$tag"166git submodule sync --recursive167git submodule update --recursive --init168readonly upstream_hash="$( git rev-parse HEAD )"169readonly upstream_hash_short="$( git rev-parse --short=8 "$upstream_hash" )"170readonly upstream_datetime="$( git rev-list "$upstream_hash" --format='%ci' -n 1 | grep -e "^$regex_date" )"171readonly upstream_date="$( echo "$upstream_datetime" | grep -o -e "$regex_date" )"172if $do_shortlog && [ -n "$basehash" ]; then173readonly commit_shortlog="174175Upstream Shortlog176-----------------177178$( git shortlog --no-merges --abbrev=8 --format='%h %s' "$upstream_old_short".."$upstream_hash" )"179else180readonly commit_shortlog=""181fi182extract_source || \183die "failed to extract source"184popd185186[ -d "$extractdir/$name-reduced" ] || \187die "expected directory to extract does not exist"188readonly commit_summary="$name $upstream_date ($upstream_hash_short)"189190# Commit the subset191pushd "$extractdir"192mv -v "$name-reduced/"* .193rmdir "$name-reduced/"194git add -A .195git commit -n --author="$ownership" --date="$upstream_datetime" -F - <<-EOF196$commit_summary197198Code extracted from:199200$repo201202at commit $upstream_hash ($tag).$commit_shortlog203EOF204git branch -f "upstream-$name"205popd206207# Merge the subset into this repository208if [ -n "$basehash" ]; then209git merge --log -s recursive "-Xsubtree=$subtree/" --no-commit "upstream-$name"210else211# Note: on Windows 'git merge --help' will open a browser, and the check212# will fail, so use the flag by default.213unrelated_histories_flag=""214if git --version | grep -q windows; then215unrelated_histories_flag="--allow-unrelated-histories "216elif git merge --help | grep -q -e allow-unrelated-histories; then217unrelated_histories_flag="--allow-unrelated-histories "218fi219readonly unrelated_histories_flag220221git fetch "$extractdir" "+upstream-$name:upstream-$name"222git merge --log -s ours --no-commit $unrelated_histories_flag "upstream-$name"223git read-tree -u --prefix="$subtree/" "upstream-$name"224fi225git commit --no-edit226git branch -d "upstream-$name"227228229