#!/bin/sh -efux # # Copyright (C) 2006-2007 Alexey Gladkov # Copyright (C) 2006 Dmitry V. Levin # Copyright (C) 2006 Sergey Vlasov # # gear-update updates subdirectory from source archive/directory. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. # . gear-sh-functions show_help() { cat < [] Utility to update subdirectory from source. Current directory will be updated if second argument is not specified. Options: -u,--unpack-dir=DIR define directory name in archive; -t,--type=TYPE define type of souece. It may on of items: 'dir', 'tar' or 'zip'; -f,--force remove files from the even if untracked or modified files found; --ignore-exclude ignore .gitignore and .git/info/exclude; -U,--unpack-all unpack all subdirectories in archive; -v,--verbose print a message for each action; -V,--version print program version and exit; -h,--help show this text and exit. Report bugs to http://bugs.altlinux.ru/ EOF exit } print_version() { cat < Copyright (C) 2006-2007 Alexey Gladkov Copyright (C) 2006 Dmitry V. Levin Copyright (C) 2006 Sergey Vlasov This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. EOF exit } source_type= get_type_by_source() { local mimetype="$(file -biz "$source" |tr [:upper:] [:lower:])" case "${mimetype#application/x-}" in tar*) source_type="tar" ;; zip) source_type="zip" ;; *) [ ! -d "$source" ] || source_type="dir" ;; esac } subdir= unpack_all= validate_subdir() { local source_lscmd= n quoted_subdir case "$source_type" in tar) source_lscmd="tar -tf" ;; zip) source_lscmd="zipinfo -1" ;; dir) subdir="$source" ;; *) fatal "$source_type: unknown source type" ;; esac [ -z "$unpack_all" ] || return 0 [ -n "$subdir" ] || subdir="$($source_lscmd "$source" |sed -e 's#\(^\|/\?\)\./#\1#g' |cut -d/ -f1 |sort -u)" n="$(printf %s\\n "$subdir" |wc -l)" [ "$n" -le 1 ] || fatal "too many subdirectories: $(printf %s "$subdir" |tr \\n \ )" [ "$n" -eq 1 ] || fatal "$source: directory not found" [ -n "$source_lscmd" ] || return 0 quoted_subdir="$(quote_sed_regexp "$subdir")" $source_lscmd "$source" |grep -qs "^$quoted_subdir/\?\$" || fatal "$source: directory not found in archive" } check_destdir() { [ "$#" -eq 2 ] || fatal "check_destdir: more arguments required" [ "$2" != "." -o ! -d "$2/.git" -o ! -d "$1/.git" ] || fatal "$dstdir/.git: unable to replace git direstory" } workdir= destdir= exit_handler() { local rc=$? trap - EXIT [ -z "$workdir" ] || rm -rf -- "$workdir" if [ "$rc" -gt 0 ]; then # Rollback actions if [ -d "$destdir" ]; then find "$destdir" -mindepth 1 -maxdepth 1 ! -name '.git' -print0 | xargs -r0 rm $verbose -rf -- else rm $verbose -f -- "$destdir" fi mkdir -p -- "$destdir" cd "$destdir" git-checkout-index -a info "unable to unpack new archive" fi exit $rc } TEMP=`getopt -n $PROG -o f,t:,d:,a,v,V,h -l ignore-exclude,unpack-all,force,type:,subdir:,verbose,version,help -- "$@"` || show_usage eval set -- "$TEMP" force= ignore_exclude= while :; do case "$1" in --ignore-exclude) ignore_exclude=1 ;; -a|--unpack-all) unpack_all=1 ;; -f|--force) force=1 ;; -t|--type) shift; source_type="$1" ;; -d|--unpack-dir) shift; subdir="$1" ;; -v|--verbose) verbose=-v ;; -V|--version) print_version ;; -h|--help) show_help ;; --) shift; break ;; *) fatal "unrecognized option: $1" ;; esac shift done [ "$#" -ge 1 ] || show_usage 'Not enough arguments.' source="$(readlink -ev "$1")"; shift destdir="$(readlink -ev "${1:-.}")" [ "$source" != "$destdir" ] || fatal "\`$source' and \`$destdir' are the same place" [ -d "$destdir" ] || fatal "$destdir: directory not available" # Move to topdir cd "$(git-rev-parse --show-cdup)" topdir="$(readlink -ev .)" destdir="$destdir/" destdir="${destdir#$topdir/}" [ "$destdir" = "${destdir#/}" ] || fatal "$destdir: directory out of \`$topdir' tree." [ -n "$destdir" ] && destdir="${destdir%/}" || destdir="." git-rm -n "$destdir" >/dev/null if [ -z "$force" ]; then out="$(git diff --name-only -- "$destdir" git-ls-files --directory --others --exclude-per-directory=.gitignore -- "$destdir")" [ -z "$out" ] || fatal "$destdir: untracked or modified files found" fi [ -n "$source_type" ] || get_type_by_source validate_subdir # Trap for rollback trap exit_handler HUP INT QUIT TERM EXIT # Remove obsolete source find "$destdir" -mindepth 1 -maxdepth 1 ! -name '.git' -print0 | xargs -r0 rm $verbose -rf -- [ "$destdir" = "." ] || rmdir $verbose -- "$destdir" # Copy new sources case "$source_type" in tar) workdir="$(mktemp -d "$PROG.XXXXXXXXX")" dir="$workdir" if [ -z "$unpack_all" ]; then printf %s\\n "$subdir/" |tar -T- -C "$workdir" -xf "$source" dir="$dir/$subdir" else tar -C "$workdir" -xf "$source" fi check_destdir "$dir" "$destdir" mv -f -- "$dir" "$destdir" ;; zip) workdir="$(mktemp -d "$PROG.XXXXXXXXX")" dir="$workdir" if [ -z "$unpack_all" ]; then unzip "$source" "$subdir/*" -d "$workdir" dir="$dir/$subdir" else unzip "$source" -d "$workdir" fi check_destdir "$dir" "$destdir" mv -f -- "$dir" "$destdir" ;; dir) check_destdir "$subdir" "$destdir" cp -a -- "$subdir" "$destdir" ;; esac [ -d "$destdir" ] || fatal "$destdir: result of extraction is not a directory" if [ -n "$ignore_exclude" ]; then git-ls-files -z "$destdir" | git-update-index --remove --force-remove -z --stdin git-ls-files -z --others --modified "$destdir" | git-update-index --add ${verbose:+--verbose} -z --stdin else git-rm $verbose "$destdir" git-add $verbose "$destdir" fi