# git.sh
# ------------------------------------------------------------------------------
#
# mingw-pkg plug-in to facilitate the use of git for tracking of changes made
# to the upstream package code, and generation of patch-sets, as required for
# building with MinGW, and for creation of MinGW source distributables.
#
# ------------------------------------------------------------------------------
#
# $Id: git.sh,v bee33caae079 2020/01/07 20:24:29 keith $
#
# Written by Keith Marshall <keith@users.osdn.me>
# Copyright (C) 2019, MinGW.org Project
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ------------------------------------------------------------------------------
#
  ARCH_DEFAULT=${ARCH_DEFAULT-"mingw32"}
  STAGED_PKGROOT=${STAGED_PKGROOT-"`pwd`"}

# When running git, on behalf of this plug-in, we need to ensure that it is
# run in the top source directory, and that this corresponds to the root of
# the repository tree; running git as GIT_CMD guarantees the former, while
# git_plugin_active_branch confirms the latter, as a side effect of getting
# the identity of the active branch, (which is expected to match the name
# of the build architecture).
#
  GIT_CMD() { git -C "$PACKAGE_ABS_SRCDIR" "$@"; }

  git_plugin_active_branch() {
    GIT_ROOT=`GIT_CMD rev-parse --show-toplevel 2> /dev/null` &&
      test "x$GIT_ROOT" = "x$PACKAGE_ABS_SRCIR" ||
      die 2 "'$PACKAGE_SRCDIR' is not managed by git"
    GIT_CMD branch | awk '$1 == "*" {print $2}'
  }

# The git_plugin_initialize function hooks into the "mingw-pkg initialize"
# action, to set up the requisite git repository whence any distributable
# package images will be constructed.
#
  defn_add_hook initialize plugin git_plugin_initialize
  git_plugin_initialize() {
    phase "initialize local git repository"
    ( cd $PACKAGE_SRCDIR
      git init --quiet
      step "import upstream $PACKAGE-$VERSION on 'master' branch"; echo
      cat > .gitignore <<-EOF
	.hg*
	.cvs*
	.*ignore
	*.pkgspec
	*.patch
	EOF

      for dir in build dist
	do test -d $dir || echo "$dir/" >> .gitignore
	done

      git add .
      git commit --quiet -m "Initial import of upstream $PACKAGE-$VERSION."
      git log; echo
      step "mark initial package state with tag 'origin'"
      git tag origin
      step "set local working branch to '${ARCH-"$ARCH_DEFAULT"}'"; echo
      git checkout --quiet -b ${ARCH-"$ARCH_DEFAULT"}
    )
  }

# The git_plugin_load_package_specs function overrides the default loader,
# to ensure that the default choice of package specs file location matches
# the build architecture specific directory which corresponds to the name
# of the active git branch.
#
  action_load_package_specs() { git_plugin_load_package_specs; }
  git_plugin_load_package_specs() {
    action_prepare_package_specs
    test -f $PACKAGE_SRCDIR/arch/${ARCH="`git_plugin_active_branch 2> /dev/null \
      || defn_parameter_list $ARCH_DEFAULT`"}/$PACKAGE-$VERSION-$ARCH.pkgspec && \
      . $PACKAGE_SRCDIR/arch/$ARCH/$PACKAGE-$VERSION-$ARCH.pkgspec
  }

# The git_plugin_stage_srcdist function hooks the "mingw-pkg distribute"
# action, to take control of generating the source code package; it uses
# the content associated with the git "origin" tag as the basis for the
# original source code, then adds the architecture specific files, and
# any applied patches, which must be managed by Git-MQ.
#
  defn_set_hook stage_srcdist plugin git_plugin_stage_srcdist
  git_plugin_stage_srcdist() {
    step "check source tree integrity"
    #
    # The "mingw-pkg distribute" action is not allowed to proceed, if
    # the source code repository is contaminated by untracked files, or
    # any uncommitted changes.
    #
    ( EXIT_CODE=0
      show_status() {
	warning "$1"; printf >&2 '%s\n\n' "$warning_colour$2$unbold"
      }
      untracked="`GIT_CMD status --porcelain | grep '^??'`" && {
	show_status "untracked files in source tree..." "$untracked"
	${ignore_untracked_files-false} || EXIT_CODE=1
      }
      GIT_CMD update-index -q --ignore-submodules --refresh
      GIT_CMD diff-files --quiet --ignore-submodules || {
	uncommitted="`GIT_CMD diff-files --ignore-submodules --name-status`"
	show_status "locally modified files in source tree..." "$uncommitted"
	EXIT_CODE=1
      }
      GIT_CMD diff --quiet --cached --ignore-submodules || {
	uncommitted="`GIT_CMD diff --cached --ignore-submodules --name-status`"
	show_status "uncommitted changes staged in index..." "$uncommitted"
	EXIT_CODE=1
      }
      exit $EXIT_CODE
    ) || die "source tree is not clean"

    # Provided the source repository is clean, we proceed to create
    # a staged image of its distributable content, which...
    #
    STAGED_PKGDIR=${STAGED_PKGDIR-"$STAGED_PKGROOT/dist"}
    STAGED_INSTALLDIR="$STAGED_PKGDIR/staged"
    rm -rf "${STAGED_SRCDIR=$STAGED_PKGDIR/$PACKAGE-$VERSION}"
    mkdir -p "$STAGED_SRCDIR/arch/${ARCH=`git_plugin_active_branch`}"
    ( cd "$PACKAGE_SRCDIR"
      ${MINGW_PORT_DISTRIBUTION-false} || {
      # ...in the case of a full distribution, is initially populated
      # with an image of the original (upstream) package content...
      #
	echo; step "replicate original source tree"
	git archive origin | (cd "$STAGED_SRCDIR" && tar xf -)

	# ...subsequently adding any third party patches...
	#
	for patchfile in arch/any/*
	do test -f $patchfile && {
	     tar chf - --hard-dereference arch/any | (
	       cd "$STAGED_SRCDIR" && tar xf -
	     )
	     break
	   }
	done
      }

      # ...and, for either a full distribution, or a mingw-port,
      # any architecture specific files which we have added, (but
      # excluding any unmanaged patches)...
      #
      test -f arch/$ARCH/$PACKAGE-$VERSION-$ARCH.pkgspec && {
	echo; step "add mingw-pkg specific arch/$ARCH files"
	tar chf - --hard-dereference --exclude='*.patch' arch/$ARCH/* | (
	  cd "$STAGED_SRCDIR" && tar xvf -
	)
	# ...and finally, the sequence of applied Git-MQ patches, if any.
	#
	local PATCHLIST="`GIT_CMD qapplied --verbose 2> /dev/null`"
	test -n "$PATCHLIST" && {
	  echo; step "export locally applied patches"
	  local GIT_ROOT="`GIT_CMD rev-parse --show-toplevel`"
	  local GIT_PATCHDIR="$GIT_ROOT/`GIT_CMD rev-parse --git-dir`/patches"
	  local STAGED_PATCHDIR="$STAGED_SRCDIR/arch/$ARCH"
	  rm -f "$STAGED_PATCHDIR"/*.patch
	  cd "$STAGED_PATCHDIR" && echo "$PATCHLIST" | awk '
	    $2 == "A" { patch[top = 1 + $1] = $3; }
	    END { if((width = 1 + int( log( top ) / log( 10 ))) < 2) width = 2;
	      cmd = "ln -fs \42'"$GIT_PATCHDIR"'/%3$s\42 \42./%2$0*1$d-%3$s\42";
	      for( idx = 1; top >= idx; idx++ ) if( idx in patch )
	      { printf "%2$0*1$d-%3$s\n", width, idx, patch[idx];
		system( sprintf( cmd, width, idx, patch[idx] ) );
	      }
	    }
	  '
	}
      }
    )
  }
#
# ------------------------------------------------------------------------------
# $RCSfile: git.sh,v $: end of file
