#!/bin/sh
# tla-commit-merge -- Commit a merge changeset
#
#  Copyright (C) 2004  Miles Bader <miles@gnu.org>
#
# This program 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.
#
# Written by Miles Bader <miles@gnu.org>
#-
# tla-commit-merge is just like `tla commit', except that it automatically
# makes an appropriate log message, of the form:
#
#    Summary: Merge from $MERGE_VERSIONS
#
#    $MERGE_LOG
# 
# MERGE_LOG is constructed from `tla log-for-merge | tla-abbrev-merge-log'
# and MERGE_VERSIONS is the arch version identifier for the merged branch (or
# branches, if more than one is merged in the same commit); tla-commit-merge
# will abort if there are no merges present.  If the archive or version
# component of MERGE_VERSIONS is the same as the current tree-version, only
# the unique portion is used.
#
# The log format used by tla-commit-merge can be customized by providing
# an executable script called `{arch}/+commit-merge-make-log' or
# `{arch}/=commit-merge-make-log'.  If either of these is found, it is
# run, and its output used as the log (note it's the entire log, so it
# should include a Summary: line).  The following environment variables
# are pre-defined to make writing such scripts more convenient:
#
#    MERGE_SUMMARY   -- The standard summary line (not including "Summary:")
#    MERGE_VERSIONS  -- A comma-separated list of versions merged
#    MERGE_LOG       -- The standard merge log body
#

# (---- beginning of hdr.shpp ----)
# hdr.shpp

me=`basename $0`

bindir='/usr/bin'
AWK='/usr/bin/nawk'; export AWK
TLA='tla'; export TLA
SED='/bin/sed'; export SED
UUIDGEN='uuidgen'; export UUIDGEN

# (---- TLA_TOOLS_VERSION defined from ,tla-tools-version ----)
TLA_TOOLS_VERSION='unknown-version
'
# (---- end of TLA_TOOLS_VERSION defined from ,tla-tools-version ----)

TLA_TOOL_PFX="${bindir+$bindir/}"
export TLA_TOOL_PFX

TLA_ESCAPE='yes'

if test "$TLA_ESCAPE" = yes; then
  TLA_UNESCAPED_OPT='--unescaped'
else
  TLA_UNESCAPED_OPT=''
fi

# Some tools get completely confused in stupid ways by non-default
# settings of LANG (like gawk, which fucks up regexp character ranges).
LANG=C; export LANG

# (---- end of hdr.shpp ----)
# (---- beginning of simple-cmd-line.shpp ----)
# simple-cmd-line.shpp -- Simple command-line processing for no-option commands

# (---- beginning of cmd-line.shpp ----)
# cmd-line.shpp -- Command-line helper functions for shell scripts

script="$0"
case "$script" in
  */*) ;;
  *)   script="${TLA_TOOL_PFX}$script";;
esac

usage ()
{
  $SED -n -e '/^\([^#]\|#-* *$\)/{s@.*@Usage: '"$me"' [--help|--version]@p;q;}'	\
         -e '/^# *Usage:/,/^# *$/{s/^# //p;q;}'				\
     < "$script"
}

short_help ()
{
  $SED -n -e '/^\([^#]\|-*# *$\|# *Usage:\)/q'				\
	 -e '/^#!/d;s/^.*-- */# /;s/^#[ 	]*//p'			\
     < "$script" | fmt
}

help_body ()
{
  $SED -n '/^ *$/q;/^#-/,/^[^#]/s/^#\( \|$\)//p' < "$script"
}

help ()
{
  usage
  short_help
  echo ''
  help_body
}

version ()
{
  local no_nl_vers=`echo "$TLA_TOOLS_VERSION"`
  echo "$me (tla-tools) $no_nl_vers"
  $SED -n '/^[^#]/q;/^#-/q;s/^# *\(Written by\)/\
\1/p' < "$script"
  $SED -n '/^[^#]/q;/^#-/q;s/^# *\(Copyright\)/\
\1/p' < "$script"
}

unrec_opt ()
{
  echo 1>&2 "$me: unrecognized option "\`"$1'"
  echo 1>&2 "Try "\`"$me --help' for more information."
}

cmd_line_err ()
{
  usage 1>&2
  echo 1>&2 "Try "\`"$me --help' for more information."
}

long_opt_val ()
{
  echo "$1" | $SED 's/^[^=]*=//'
}

short_opt_val ()
{
  echo "$1" | $SED 's/^-.//'
}

# (---- end of cmd-line.shpp ----)

case "$1" in
  --help)
    usage
    short_help
    echo ''
    echo "      --help           Display this help message and exit"
    echo "      --version        Display a release identifier string and exit"
    echo ''
    help_body
    exit 0
    ;;
  --version|-V)
    version; exit 0;;
  --)
    shift;;
  -*)
    unrec_opt "$1"; exit 1;;
esac

# (---- end of simple-cmd-line.shpp ----)

test $# -eq 0 || { cmd_line_err; exit 1; }

tree_ver=`$TLA tree-version` || exit 5
tree_ver_arch=`dirname "$tree_ver"`
tree_ver_vers=`basename "$tree_ver"`

tmp_pfx=",,commit-merge.$$"
trap "rm -f '$tmp_pfx'*" 0 1 2 3 11 15

dep_revs="$tmp_pfx.dep"

# Compute the set of revisions which are actually merged into other revisions
for mv in `$TLA new-merges`; do
  tla cat-log "$mv" | $AWK '
    /^$/ { exit (0) }

    /^Revision:/ { rev = $2; next }
    /^Archive:/ { arch = $2; next }

    function print_patches(first_field)
    {
      for (; first_field <= NF; first_field++)
	if ($first_field != arch "/" rev)
	  print $first_field
    }

    $1 == "New-patches:" { in_new_patches_hdr = 1; print_patches(2); next }
    in_new_patches_hdr && /^[ \t]/ { print_patches(1); next }
    in_new_patches_hdr { exit (0) }
  '
done | sort -u > "$dep_revs"

# Compute the set of versions for which there's a revision no other
# revision has merged.  These are "directly merged" versions.
merged_versions="$tmp_pfx.merged"
$TLA new-merges | sort -u | comm -13 "$dep_revs" -	\
  | sed 's@\([^/]*/[^/]*--[^/]*--[^/]*\)--[^/]*@\1@'    \
  | uniq > "$merged_versions"

# number of merged versions
num_merged_versions=`wc -l < "$merged_versions"`

if test $num_merged_versions -eq 0; then
  echo 1>&2 "$me: Nothing merged?!?"
  exit 10
fi

if test $num_merged_versions -gt 3; then
  # multiple merged versions
  echo 1>&2 "$me: too many versions merged, aborting:"
  $SED 's/^/   /' 1>&2 < "$merged_versions"
  exit 20
fi

for merge_ver in `cat "$merged_versions"`; do
  merge_ver_arch=`dirname "$merge_ver"`
  merge_ver_vers=`basename "$merge_ver"`

  if test x"$tree_ver_arch" = x"$merge_ver_arch"; then
    desc="$merge_ver_vers"
  elif test x"$tree_ver_vers" = x"$merge_ver_vers"; then
    desc="$merge_ver_arch"
  else
    desc="$merge_ver"
  fi

  if test x"$merge_desc" = x; then
    merge_desc="$desc"
  else
    merge_desc="$merge_desc, $desc"
  fi
done

log="$tmp_pfx.log"

(
  merge_summary="Merge from $merge_desc"

  for pfx in + =; do
    make_log_script="{arch}/${pfx}commit-merge-make-log"
    test -r "$make_log_script" && break
  done
  if test -r "$make_log_script"; then
    MERGE_VERSIONS="$merge_desc"
    export MERGE_VERSIONS
    MERGE_SUMMARY="$merge_summary"
    export MERGE_SUMMARY
    MERGE_LOG=`$TLA log-for-merge | ${TLA_TOOL_PFX}tla-abbrev-merge-log -A"$tree_ver_arch"`
    export MERGE_LOG

    if "$make_log_script"; then
      :
    else
      echo 1>&2 "$me: $make_log_script failed, aborting commit"
      exit 49
    fi
  else
    echo "Summary: $merge_summary"
    echo ""
    $TLA log-for-merge | ${TLA_TOOL_PFX}tla-abbrev-merge-log -A"$tree_ver_arch"
  fi
) > "$log" || exit $?

$TLA commit -l "$log" "$@"

