#
# commit.sh: archive a pre-patch revision
################################################################
# Copyright (C) 2001, 2002 Tom Lord
# 
# See the file "COPYING" for further information about
# the copyright and warranty status of this work.
# 

set -e 

command_line="$*"

################################################################
# special options
# 
# Some options are special:
# 
#       --version | -V
#       --help | -h
# 
if test $# -ne 0 ; then

  for opt in "$@" ; do
    case $opt in

      --version|-V) exec larch --version
                    ;;

      --help|-h)
                printf "archive a revision\\n"
                printf "usage: commit [options] [[archive/]version]\\n"
                printf "usage: commit --files [options] [[archive/]version] -- file ...\\n"
                printf "usage: commit --file-list FILE-LIST [options] [[archive/]version]\\n"
                printf "\\n"
                printf " -V --version                  print version info\\n"
                printf " -h --help                     display help\\n"
                printf "\\n"
                printf " -R --root root                specify the local archive root\\n"
                printf " -A --archive archive          specify the archive name\\n"
                printf "\\n"
                printf " -L --log-file file            specify the log-file\\n"
                printf " --not-default                 do not make this the default branch\\n"
                printf " --no-lint                     do not tree-lint the project tree\\n"
                printf " --strict                      strict tree-lint (warnings->errors)\\n"
                printf "\\n"
                printf " -d --dir DIR                  cd to DIR first\\n"
                printf "\\n"
                printf " --silent                      no output (except odd errors)\\n"
                printf " --quiet                       brief output\\n"
                printf " --report                      default output\\n"
                printf " --verbose                     maximal output\\n"
                printf " --debug                       debugging output\\n"
                printf "\\n"
                printf " --seal                        commit the version revision\\n"
                printf " --fix                         commit a versionfix- patch level\\n"
                printf " --continuation [archive/]prev-revision  commit a continuation revision\\n"
                printf " --out-of-date-ok              permit commit even if project tree is\\n"
                printf "                                 not up to date\\n"
                printf " --unchanged-ok                commit even if project tree is\\n"
                printf "                                 unmodified\\n"
                printf "\\n"
                printf " --dry-run                     don't actually commit -- just\\n"
                printf "                               prepare the patch set\\n"
                printf "\\n"
                printf " --explicit                    force the explicit tagging method\\n"
                printf " --implicit                    force the implicit tagging method\\n"
                printf " --names                       force the name-based tagging method\\n"
                printf "\\n"
                printf " --files                       (note syntax above) restrict commit to\\n"
                printf "                                 selected files from the command line\\n"
                printf " --file-list FILE-LIST         restrict commit to the files named\\n"
                printf "                                 in FILE-LIST (one per line)\\n"
                printf "\\n"
                printf "Archive a revision of the project tree containing DIR (or the\\n"
                printf "current directory)\\n"
                printf "\\n"
                printf "If there are any automated ChangeLog files, update them before\\n"
                printf "computing the patch set.\\n"
                printf "\\n"
                printf "Ordinarily no commit will occur if the only changes to the project\\n"
                printf "tree are the new log entry and changes to ChangeLog file.  To force\\n"
                printf "such a commit to occur anyway, use \"--unchanged-ok\".\\n"
                printf "\\n"
                printf "If --files or --file-list is used, the indicated files must exist in\\n"
                printf "both the project tree and the immediate ancestor revision (though they\\n"
                printf "may have been renamed).  The resulting commit will include file differences\\n"
                printf "only -- not tree rearrangements.\\n"
                printf "\\n"
                printf "If the file specified to --file-list is -, the list is read from standard\\n"
                printf "input.\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

################################################################
# Ordinary Options
# 
# 

archroot=
archive=

no_lint=
strict=
makedefault=1
logfile=
continuation=0
continuation_revision=
fix=0
seal=0
out_of_date_ok=0
dry_run=
unchanged_ok=
__restart=

quiet=--quiet
report=--report
verbose=
silent_opt=
quiet_opt=
report_opt=
verbose_opt=
debug_opt=

method=

selected_files=
file_list=

dir=.

while test $# -ne 0 ; do

  case "$1" in 

    -d|--dir)           shift
                        if test $# -eq 0 ; then
                          printf "commit: -d and --dir require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        dir="$1"
                        shift
                        ;;

     --explicit)        shift
                        method=--explicit
                        ;;

     --implicit)        shift
                        method=--implicit
                        ;;

     --names)           shift   
                        method=--names
                        ;;

     --file-list)	shift
     			if test $# -eq 0 ; then
                          printf "commit: --file-list requires an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        file_list="$1"
                        shift
                        selected_files=yes
                        ;;

     --files)		shift
                        selected_files=--files
                        file_list=
                        ;;

     ----restart) shift
                 __restart=----restart
                 ;;

     --no-lint)  shift
                 no_lint=--no-lint
                 strict=
                 ;;

     --strict)   shift
                 no_lint=
                 strict=--strict
                 ;;

     --silent)  shift
                 quiet=
                 report=
                 verbose=
                 silent_opt=--silent
                 quiet_opt=
                 report_opt=
                 verbose_opt=
                 ;;

    --quiet)    shift
                quiet=--quiet
                report=
                verbose=
                silent_opt=
                quiet_opt=--quiet
                report_opt=
                verbose_opt=
                ;;

    --report)   shift
                quiet=--quiet
                report=--report
                verbose=
                silent_opt=
                quiet_opt=
                report_opt=--report
                verbose_opt=
                ;;

    --verbose)  shift
                quiet=--quiet
                report=--report
                verbose=--verbose
                silent_opt=
                quiet_opt=
                report_opt=
                verbose_opt=--verbose
                ;;

    --debug)    shift
                larch heading "commit: debugging output enabled\\n"
                set -x
                debug_opt=--debug
                ;;


    -R|--root)          shift
                        if test $# -eq 0 ; then
                          printf "commit: -R and --root require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        archroot="$1"
                        shift
                        ;;

    -A|--archive)       shift
                        if test $# -eq 0 ; then
                          printf "commit: -A and --archive require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        archive="$1"
                        shift
                        ;;

    -L|--log-file)      shift
                        if test $# -eq 0 ; then
                          printf "commit: -L and --log-file require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        logfile="$1"
                        shift
                        ;;

    --not-default)      shift
                        makedefault=0
                        ;;

    --fix)              shift
                        fix=1
                        seal=0
                        continuation=0
                        ;;

    --seal)             shift
                        fix=0
                        seal=1
                        continuation=0
                        unchanged_ok=--unchanged-ok
                        ;;

    --continuation)     shift
                        fix=0
                        seal=0
                        continuation=1
                        unchanged_ok=--unchanged-ok
                        if test $# -eq 0 ; then
                          printf "commit: --continuation requires an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        continuation_revision="$1"
                        shift
                        ;;

    --out-of-date-ok)   shift
                        out_of_date_ok=1
                        ;;

    --unchanged-ok)     shift
                        unchanged_ok=--unchanged-ok
                        ;;

    --dry-run)          shift
                        dry_run=--dry-run
                        ;;

    --)			break
                        ;;

    -*)                 printf "commit: unrecognized option (%s)\\n" "$1" 1>&2
                        printf "try --help\\n" 1>&2
                        exit 1
                        ;;

    *)                  break
                        ;;
  esac

done



################################################################
# Ordinary Arguments
# 

if test -z "$selected_files" -a '(' $# -gt 1 ')' ; then
  printf "usage: commit [options] [[archive]/version]\\n" 1>&2
  printf "try --help\\n" 1>&2
  exit 1
fi

if test '(' $# -gt 0 ')' -a '(' "$1" != -- ')' ; then
  archive_version="$1"
  shift
else
  archive_version=
fi

if test ! -z "$selected_files" ; then

  if test -z "$file_list" ; then

    if test '(' $# -eq 2 ')' -o '(' "$1" = -- ')' ; then
      shift
    else
      printf "usage: commit --files [options] [[archive]/version] -- file ...\\n" 1>&2
      printf "try --help\\n" 1>&2
      exit 1
    fi
  else
    
    if test $# -ne 0 ; then
      printf "usage: commit --file-list FILE [options] [[archive]/version]\\n" 1>&2
      printf "try --help\\n" 1>&2
      exit 1
    fi
  fi

fi


################################################################
# Sanity Check and Process Defaults
# 
  
start_dir="`pwd`"

cd "$dir"
dir="`pwd`"

wdroot="`larch tree-root --accurate`"
cd "$wdroot"
  
if test "x$archive_version" = x ; then
  archive_version=`larch tree-version`
else
  larch valid-package-name -e commit --vsn -- "$archive_version"
fi

archive=`larch parse-package-name -R "$archroot" -A "$archive" --arch $archive_version`
version=`larch parse-package-name -R "$archroot" -A "$archive" --package-version $archive_version`
category=`larch parse-package-name --basename $version`
branch=`larch parse-package-name $version`
vsn=`larch parse-package-name -v $version`

if test -z "$logfile" ; then
  logfile="$wdroot/++log.$version--$archive"
fi

cd "$start_dir"
logfile_dir="`dirname \"$logfile\"`"
logfile_base="`basename \"$logfile\"`"
cd "$logfile_dir"
logfile="`pwd`/$logfile_base"

# Make sure the wd has a patch log for this version.
# 
larch wd-check-for-patch-log -e commit -R "$archroot" -A "$archive" "$version" "$wdroot"

# Make sure we have a valid log file for this revision.
# 
larch valid-log-file -e commit -- "$logfile"

tmpdir="$wdroot/,,commit-tmp-dir"


################################################################
# Greetings
# 

if test "(" -z "$__restart" -a ! -z "$quiet" ")" -o ! -z "$report" ; then
  larch heading "commit\\n"
  printf "arguments: %s\\n"  "$command_line" | fold -w 60 | larch body-indent
  larch heading --sub "commit start time: %s\\n" "`date`"
  larch heading --sub "project tree: %s\\n" "$wdroot"
  larch heading --sub "archive: %s\\n" "$archive"
  larch heading --sub "version: %s\\n" "$version"
  if test ! -z "$continuation_revision" ; then
    larch heading --sub "this is a continuation revision (--continuation)\\n"
  elif test $fix -ne 0 ; then
    larch heading --sub "this is versionfix revision (--fix)\\n"
  elif test $seal -ne 0 ; then
    larch heading --sub "this is version revision (--seal)\\n"
  fi
  if test ! -z "$dry_run" ; then 
    larch heading --sub "this is a dry run (no modifications to the archive)\\n"
  fi
fi


################################################################
# Ensure that We Have an Archive Connection 
# 

if test "$WITHARCHIVE" != "$archive" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub "restarting with connection to archive\\n"
  fi

  flags=

  if test $makedefault -eq 0 ; then
    flags="-d $flags"
  fi

  if test $fix -ne 0 ; then
    flags="--fix $flags"
  elif test $seal -ne 0 ; then
    flags="--seal $flags"
  elif test $continuation -ne 0 ; then
    flags="--continuation $continuation_revision $flags"
  fi

  if test $out_of_date_ok -ne 0 ; then
    flags="--out-of-date-ok $flags"
  fi

  cd "$start_dir"

  if test -z "$file_list" ; then

    if test ! -z "$selected_files" ; then
      dashdash="--"
    else
      dashdash=
    fi

    exec larch with-archive -R "$archroot" -A "$archive" \
        larch commit -R "$archroot" -A "$archive" \
                    -L "$logfile" \
                    $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
                    $unchanged_ok \
                    $no_lint \
                    $strict \
                    $flags \
                    $dry_run \
                    $method \
                    $selected_files \
                    --dir "$dir" \
                    ----restart \
                    "$archive/$version" \
                    $dashdash \
                    "$@"

  else

    exec larch with-archive -R "$archroot" -A "$archive" \
        larch commit -R "$archroot" -A "$archive" \
                    -L "$logfile" \
                    --file-list "$file_list" \
                    $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
                    $unchanged_ok \
                    $no_lint \
                    $strict \
                    $flags \
                    $dry_run \
                    $method \
                    --dir "$dir" \
                    ----restart \
                    "$archive/$version"

  fi
fi



################################################################
# Create a Temp Dir in Which to Build the Patch
# 

bail()
{
  cd "$wdroot"
  rm -rf "$tmpdir"
  exit 1
}

trap "printf \"commit: interrupted -- cleaning up\\n\" 1>&2 ; bail" INT

rm -rf "$tmpdir"
mkdir "$tmpdir"


################################################################
# Process Defaults for the Continuation Revision
# 

first()
{
  if test $# = 0 ; then
    printf "first called on empty list\\n" 1>&2
    bail
  else
    printf "%s\\n" "$1" 
  fi
}

second()
{
  if test $# -lt 2 ; then
    printf "first called on short list\\n" 1>&2
    bail
  else
    printf "%s\\n" "$2" 
  fi
}

if test ! -z "$continuation_revision" ; then


  indicated_continuation="`larch indicated-revision -e commit -R \"$archroot\" -A \"$archive\" \"$continuation_revision\"`"

  continuation_archive=${indicated_continuation%%/*}
  continuation_revision=${indicated_continuation#*/}

  continuation_category=`larch parse-package-name --basename $continuation_revision`
  continuation_branch=`larch parse-package-name $continuation_revision`
  continuation_vsn=`larch parse-package-name --vsn $continuation_revision`
  continuation_lvl=`larch parse-package-name --patch-level $continuation_revision`

fi



################################################################
# What Revision Comes Next?
# 


preclude_fix()
{
  if test $fix -ne 0 ; then
    printf "commit: too early for versionfix patches\\n" 1>&2
    printf "\\n" 1>&2
    printf "  version-0 patch level doesn't exist yet.\\n" 1>&2
    printf "\\n" 1>&2
    printf "Try \"--seal\" (see \"larch commit --help\").\\n" 1>&2
    printf "\\n" 1>&2
    bail
  fi
}

all_levels="`larch revisions -R \"$archroot\" -A \"$archive\" --reverse \"$version\"`"

if test "x$all_levels" = x ; then

  if test $continuation -ne 0 ; then
    lvl=base-0
  else
    printf "commit: version has no revisions yet\\n" 1>&2
    printf "\\n" 1>&2
    printf "  version: %s\\n" $version 1>&2
    printf "\\n" 1>&2
    printf "Try \"larch import --help\" or \"commit --continuation\" instead.\\n" 1>&2
    printf "\\n" 1>&2
    bail
  fi

else

  latest_lvl=`first $all_levels`

  case "$latest_lvl" in
    base-0)             preclude_fix
                        if test $seal -eq 0 ; then
                          lvl=patch-1
                        else
                          lvl=version-0
                        fi
                        ;;

    patch-*)            preclude_fix
                        if test $seal -eq 0 ; then
                          lvl="patch-$((${latest_lvl#patch-} + 1))"
                        else
                          lvl=version-0
                        fi
                        ;;

    version-0|versionfix-*)

                        lvl="versionfix-$((${latest_lvl##*-} + 1))"
                        if test $fix -eq 0 ; then
                          printf "commit: too late for ordinary patches\\n" 1>&2
                          printf "\\n" 1>&2
                          printf "  version-0 already exists.\\n" 1>&2
                          printf "\\n" 1>&2
                          printf "Try \"--fix\" (see \"larch commit --help\").\\n" 1>&2
                          printf "\\n" 1>&2
                          bail
                        fi
                        ;;

    
  esac

fi


# Conditions
#
#   lvl set to the patch level name for the revision 
#     we should create
# 
#   latest_lvl set to the patch level name of the previous revision 
# 
################################################################
# Is the Working Dir Sufficiently Up to Date
# 
# 
#

if test $out_of_date_ok -eq 0 ; then

  cd "$wdroot"

  missing=

  for rev in $all_levels ; do
    if test ! -e "{arch}/$category/$branch/$version/$archive/patch-log/$rev" ; then
      missing="$rev $missing"
    fi
  done

  if test "x$missing" != x ; then
    printf "commit: project tree out of date\\n" 1>&2
    printf "\\n" 1>&2
    printf "  Working directory is missing these patches:\n" 1>&2
    printf "\\n" 1>&2
    for m in $missing ; do
      printf "\t%s\\n" $m 1>&2
    done
    printf "\\n" 1>&2
    printf "  Unable to complete \"commit\".\\n" 1>&2
    printf "\\n" 1>&2
    printf "Try \"larch update --help\" or \"larch commit --out-of-date-ok\"\\n" 1>&2
    printf "\\n" 1>&2
    bail 
  fi

fi


################################################################
# Lint the Source Tree
# 

if test -z "$no_lint" ; then
  cd "$wdroot"

  if test ! -z "$quiet" ; then
    larch nested larch tree-lint $strict
  else
    larch tree-lint $strict > /dev/null
  fi
fi



################################################################
# Get a Pristine Copy of the Previous Revision to Compare With
# 

cd "$tmpdir"

if test ! -z "$report" ; then
  larch heading --sub "finding (or making) previous revision for comparison\\n"
  if test ! -z "$verbose" ; then
    larch heading --sub --sub "start time: %s\\n" "`date`"
  fi
fi

if test $continuation -eq 0 ; then

  prev_archive="$archive"
  prev_category="$category"
  prev_branch="$branch"
  prev_vsn="$vsn"
  prev_lvl="$latest_lvl"

else

  prev_archive="$continuation_archive"
  prev_category="$continuation_category"
  prev_branch="$continuation_branch"
  prev_vsn="$continuation_vsn"
  prev_lvl="$continuation_lvl"

fi

if test -e "$wdroot/{arch}/++pristine-trees/locked/$prev_category/$prev_branch/$prev_branch--$prev_vsn/$prev_archive/$prev_branch--$prev_vsn--$prev_lvl" ; then

  prev_tree="$wdroot/{arch}/++pristine-trees/locked/$prev_category/$prev_branch/$prev_branch--$prev_vsn/$prev_archive/$prev_branch--$prev_vsn--$prev_lvl"

elif test -e "$wdroot/{arch}/++pristine-trees/unlocked/$prev_category/$prev_branch/$prev_branch--$prev_vsn/$prev_archive/$prev_branch--$prev_vsn--$prev_lvl" ; then

  prev_tree="$wdroot/{arch}/++pristine-trees/unlocked/$prev_category/$prev_branch/$prev_branch--$prev_vsn/$prev_archive/$prev_branch--$prev_vsn--$prev_lvl"

elif larch library-find --silent -A "$prev_archive" "$prev_branch--$prev_vsn--$prev_lvl" > /dev/null ; then

  prev_tree="`larch library-find -A \"$prev_archive\" \"$prev_branch--$prev_vsn--$prev_lvl\"`"

else

  mkdir "$tmpdir/,,prev_tree"
  cd  "$tmpdir/,,prev_tree"

  if ! larch nested \
           larch build-revision -R "$archroot" -A "$prev_archive" \
                               --silent $quiet_opt $report_opt $verbose_opt $debug_opt \
                               --cache "$wdroot/.." "$prev_branch--$prev_vsn--$prev_lvl" ; then
    bail
  fi

  cd "$tmpdir"
  larch wd-mv-pristine "$prev_archive" "$prev_branch--$prev_vsn--$prev_lvl" ,,prev_tree "$wdroot"
  prev_tree="$wdroot/{arch}/++pristine-trees/unlocked/$prev_category/$prev_branch/$prev_branch--$prev_vsn/$prev_archive/$prev_branch--$prev_vsn--$prev_lvl"

fi

if test ! -z "$verbose" ; then
  larch heading --sub --sub "finish time: %s\\n" "`date`"
fi

# Conditions
#
#   prev_tree points to a pristine tree for the previous revision
# 
################################################################
# Smudge the ChangeLogs
# 
# Sleaze, pure and simple -- to force the ChangeLogs to show up in 
# the new or modified files lists of the patch set.
# 

if test ! -z "$report" ; then
  larch heading --sub "finding automated ChangeLogs\\n"
fi

cd "$wdroot"

larch inventory $method --source --all --files --tags | sort -k 2 > "$tmpdir/,,mod-tree-files-index"


archive_re="[-a-zA-Z0-9]+(\\.[-a-zA-Z0-9]*)*@[-a-zA-Z0-9.]*(--[-a-zA-Z0-9.]*)?" 
base_re="([a-zA-Z]([a-zA-Z0-9]*(-[a-zA-Z0-9]+)?)*)"
vsn_re="([0-9]+\\.[0-9]+)"
changelog_tag_re="[ 	][ix]_automatic-ChangeLog--($archive_re)/($base_re)(--$base_re)?(--$vsn_re)$"


touch "$tmpdir/,,automated-changelogs-key"

for cl_pair in `cat "$tmpdir/,,mod-tree-files-index" | grep -E -e "$changelog_tag_re" | awk '{ sub("^[ix]_automatic-ChangeLog--", "", $2); printf "%s,%s\n", $2, $1; }'` ; do

  cl_version=${cl_pair%%,*}
  cl=${cl_pair#*,}

  if test ! -z "$cl_version" && larch wd-check-for-patch-log "$cl_version" "$tree" 2> /dev/null ; then

    if test ! -z "$report" ; then
      printf "$cl\\n" | larch body-indent --nonl --sub
    fi

    perms="`file-metadata --symlink --permissions \"$cl\"`"
    chmod u+w "$cl"
    printf "\\n" >> "$cl"
    set-file-metadata $perms "$cl"

    printf "%s\\t%s\\n" "$cl" "$cl_version" >> "$tmpdir/,,automated-changelogs-key"

  fi

done

if test ! -z "$report" ; then
  if test ! -z "`head -n 1 \"$tmpdir/,,automated-changelogs-key\"`" ; then 
    printf "\\n" | larch body-indent --sub
  else
    printf "none\\n" | larch body-indent --sub
  fi
fi


################################################################
# Make a Patch Set Between The two Trees
# 

if test ! -z "$report" ; then
  larch heading --sub "making patch set\\n" 
fi

cd "$tmpdir"

if test -z "$file_list" -a -z "$selected_files" ; then

  larch nested \
    larch mkpatch $method $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt "$prev_tree" "$wdroot" ,,patch-set

else

  rm -f "$tmpdir/,,absolute-automated-changelogs"
  touch "$tmpdir/,,absolute-automated-changelogs"

  for f in `cat "$tmpdir/,,automated-changelogs-key" | awk '{ print $1; }'` ; do
    printf "%s/%s\\n" "$wdroot" "$f" >> "$tmpdir/,,absolute-automated-changelogs"
  done


  if test -z "$file_list" ; then

    larch nested \
      larch mkpatch-files --file-root "$start_dir"  $method $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt "$prev_tree" "$wdroot" ,,patch-set "$@" `cat "$tmpdir/,,absolute-automated-changelogs"`

  else

    # --file-list case
    # 

    ( cat "$tmpdir/,,absolute-automated-changelogs"
      cat "$file_list" ) \
    | larch nested \
        larch mkpatch-files --file-list -  --file-root "$start_dir" $method $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt "$prev_tree" "$wdroot" ,,patch-set

  fi
fi

  

################################################################
# Modify the Patch Set to Include the Wrong Log File
# 

larch patch-add-log ,,patch-set "$archive/$version--$lvl" "$logfile"


################################################################
# Compute the Changed File Sets
# 
# 

cd "$wdroot"
touch "$tmpdir/,,log-ls"
for v in `larch logs` ; do
  for p in `larch log-ls $v` ; do
    printf "%s--%s\\n" $v $p >> "$tmpdir/,,log-ls"
  done
done
printf "%s/%s\\n" "$archive" "$version--$lvl" >> "$tmpdir/,,log-ls"

cd "$prev_tree"
touch "$tmpdir/,,prev-patches"
for v in `larch logs` ; do
  for p in `larch log-ls $v` ; do
    printf "%s--%s\\n" $v $p  >> "$tmpdir/,,prev-patches"
  done
done

cd "$tmpdir"

rm -f ,,tmp
mv ,,log-ls ,,tmp
sort ,,tmp > ,,log-ls

rm -f ,,tmp
mv ,,prev-patches ,,tmp
sort ,,tmp > ,,prev-patches
rm -f ,,tmp

comm -2 -3 ,,prev-patches ,,log-ls > ,,prev-only-patches
comm -1 -3 ,,prev-patches ,,log-ls > ,,wd-only-patches

cd "$tmpdir/,,patch-set/patches"
find . -type f \
| grep -E -e '\.(link-mod|modified|patch|meta-mod)$' \
| sed -e "s/\.[^.]*$//" \
| sort -u \
> "$tmpdir/,,modified-files"

cd "$tmpdir/,,patch-set/patches"
find . -type f -name "=dir-meta-mod" \
| xargs -n 1 need-args dirname \
| sort \
> "$tmpdir/,,modified-dirs"


################################################################
# Has Anything Really Changed?
# 
# 

cd "$tmpdir"

( printf "%s\\n" "./{arch}" ; \
  printf "%s\\n" "./{arch}/$category" ; \
  printf "%s\\n" "./{arch}/$category/$branch" ; \
  printf "%s\\n" "./{arch}/$category/$branch/$version" ; \
  printf "%s\\n" "./{arch}/$category/$branch/$version/$archive" ; \
  printf "%s\\n" "./{arch}/$category/$branch/$version/$archive/patch-log" ) \
| sed -e '{
            s,[/$*[\\],\\&,g
            s,^,/^,
            s,$,$/d,
          }' \
> ,,ignored-dirs-sed-script

if test  -z "$unchanged_ok" \
         -a -z "`cat ,,prev-only-patches ,,modified-dirs | head -n 1`" \
         -a  -z "`cat ,,modified-files | sed -e '/^.\/ChangeLog\(\..*\)\{0,1\}$/d' | head -n 1`" \
         -a "`cat ,,wd-only-patches`" = "$archive/$version--$lvl" \
         -a "`join -v 2 -o 2.1 -1 2 -2 2 ,,patch-set/orig-files-index ,,patch-set/mod-files-index`" = "./{arch}/$category/$branch/$version/$archive/patch-log/$lvl" \
         -a  -z "`join -v 1 -o 1.1 -1 2 -2 2 ,,patch-set/orig-files-index ,,patch-set/mod-files-index | head -n 1`" \
         -a  -z "`join -v 2 -o 2.1 -1 2 -2 2 ,,patch-set/orig-dirs-index ,,patch-set/mod-dirs-index | sed -f ,,ignored-dirs-sed-script | head -n 1`" \
         -a  -z "`join -v 1 -o 1.1 -1 2 -2 2 ,,patch-set/orig-dirs-index ,,patch-set/mod-dirs-index | head -n 1`" \
   && join -o 1.1,2.1 -1 2 -2 2 ,,patch-set/orig-files-index ,,patch-set/mod-files-index | awk '($1 != $2) { exit(1); }' \
   && join -o 1.1,2.1 -1 2 -2 2 ,,patch-set/orig-dirs-index ,,patch-set/mod-dirs-index | awk '($1 != $2) { exit(1); }' \
; then

  # nothing changed -- without the --unchanged-ok option
  # 
  if test ! -z "$quiet" ; then
    larch heading --sub "project tree unchanged except for log entries\\n"
    printf "no commit will take place (use --unchanged-ok to override)\\n" | larch body-indent --sub
    larch heading --sub "commit finish time: %s\\n" "`date`"
    larch heading --sub "commit exit status: 0\\n"
  fi

  cd "$wdroot"
  rm -rf "$tmpdir"
  exit 0
fi

################################################################
# Fix Up the Log File
# 

sort_fqrn_list()
{
  sed -e 's+\(.*\)/\(.*\)--\(.*\)--\([0-9]*\)\.\([0-9]*\)--\(.*\)-\([0-9]*\)+\1,\2,\3,\4,\5,\6,\7+' \
      -e 's+\(.*\)/\(.*\)--\([0-9]*\)\.\([0-9]*\)--\(.*\)-\([0-9]*\)+\1,\2,,\3,\4,\5,\6+' \
  | sort -u -t, -k 1,1 -k2,2 -k3,3 -k4,4n -k5,5n -k6,6 -k7,7n \
  | sed -e 's+^\(.*\),\(.*\),,\(.*\),\(.*\),\(.*\),\(.*\)+\1/\2--\3.\4--\5-\6+' \
        -e 's+^\(.*\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\),\(.*\)+\1/\2--\3--\4.\5--\6-\7+' 
}

if test ! -z "$report" ; then
  larch heading --sub "mangling the log file\\n"
fi

cd "$wdroot"
cp "$logfile" "$tmpdir/,,raw-log"

cd "$tmpdir"

(   printf "Revision: %s\\n" $version--$lvl \
 && printf "Archive: %s\\n" $archive \
 && printf "Creator: %s\\n" "`larch my-id`" \
 && printf "Date: %s\\n" "`date`" \
 && printf "Standard-date: %s\\n" "`date -u \"+%Y-%m-%d %H:%M:%S GMT\"`" \
 && sed -e "/^\$/,\$d" ,,raw-log \
 && (  join -v 2 -o 2.1 -1 2 -2 2 ,,patch-set/orig-files-index ,,patch-set/mod-files-index \
     | sed -e 's,^\./,,' \
     | unfold -w 60 \
     | sed -e "1s/^/New-files: /" -e "1!s/^/  /" ) \
 && (  join -v 2 -o 2.1 -1 2 -2 2 ,,patch-set/orig-dirs-index ,,patch-set/mod-dirs-index \
     | sed -e 's,^\./,,' \
     | unfold -w 60 \
     | sed -e "1s/^/New-directories: /" -e "1!s/^/  /" ) \
 && (   join -v 1 -o 1.1 -1 2 -2 2 ,,patch-set/orig-files-index ,,patch-set/mod-files-index \
      | sed -e 's,^\./,,' \
      | unfold -w 60 \
      | sed -e "1s/^/Removed-files: /" -e "1!s/^/  /" ) \
 && (  join -v 1 -o 1.1 -1 2 -2 2 ,,patch-set/orig-dirs-index ,,patch-set/mod-dirs-index \
     | sed -e 's,^\./,,' \
     | unfold -w 60 \
     | sed -e "1s/^/Removed-directories: /" -e "1!s/^/  /" ) \
 && (  cat ",,patch-set/mod-files-index" \
     | awk -f "$ARCH_SUBCMD_ROOT/patch-sets/compute-renamed.awk" \
           -v orig_dir_index=",,patch-set/orig-dirs-index" \
           -v mod_dir_index=",,patch-set/mod-dirs-index" \
           -v orig_X_index=",,patch-set/orig-files-index" \
     | cut -s -d ' ' -f 1,2 \
     | sed -e 's,^\./,,' -e 's, ./, ,' \
     | unfold -w 60 \
     | sed -e "1s/^/Renamed-files: /" -e "1!s/^/  /" ) \
 && (  cat ",,patch-set/mod-dirs-index" \
     | awk -f "$ARCH_SUBCMD_ROOT/patch-sets/compute-renamed.awk" \
           -v orig_dir_index=",,patch-set/orig-dirs-index" \
           -v mod_dir_index=",,patch-set/mod-dirs-index" \
           -v orig_X_index=",,patch-set/orig-dirs-index" \
     | cut -s -d ' ' -f 1,2 \
     | sed -e 's,^\./,,' -e 's, ./, ,' \
     | unfold -w 60 \
     | sed -e "1s/^/Renamed-directories: /" -e "1!s/^/  /" ) \
 && ( cat ,,modified-files | sed -e 's,^\./,,' | unfold -w 60 | sed -e "1s/^/Modified-files: /" -e "1!s/^/  /" ) \
 && ( cat ,,modified-dirs | sed -e 's,^\./,,' | unfold -w 60 | sed -e "1s/^/Modified-directories: /" -e "1!s/^/  /" ) \
 && ( cat ,,wd-only-patches | sort_fqrn_list | unfold -w 60 | sed -e "1s/^/New-patches: /" -e "1!s/^/  /" ) \
 && ( cat ,,prev-only-patches | sort_fqrn_list | unfold -w 60 | sed -e "1s/^/Removed-patches: /" -e "1!s/^/  /" ) )\
> ,,cooked-log

if test $continuation -ne 0 ; then
  printf "Continuation-of: %s\\n" "$continuation_archive/$continuation_branch--$continuation_vsn--$continuation_lvl" >> ,,cooked-log
fi

(   printf "\\n" \
 && sed -e "1,/^\$/d" ,,raw-log )\
>> ,,cooked-log

if test ! -z "$quiet" ; then
  sed -e "/^\$/,\$d" ,,cooked-log | larch body-indent --sub
fi

################################################################
# Put the Right Log File in the Patch Set
# 

cd "$tmpdir"
new_file_in_patch=",,patch-set/new-files-archive/{arch}/$category/$branch/$version/$archive/patch-log/$lvl"
rm -f "$new_file_in_patch"
cp ,,cooked-log "$new_file_in_patch"


################################################################
# Fix Up the ChangeLogs as they Appears in the Patch Set
# 

if test ! -z "$report" ; then
  larch heading --sub "updating automated ChangeLogs\\n"
fi

cd "$wdroot"

sort -k 1 "$tmpdir/,,patch-set/mod-files-index" > "$tmpdir/,,mod-files-index-by-name"

for cl_pair in `cat "$tmpdir/,,automated-changelogs-key" | awk '{ printf "%s////%s\n", $1, $2; }'` ; do

  cl="${cl_pair%%////*}"
  cl_version="${cl_pair#*////}"

  if test ! -z "$report" ; then
    printf "$cl" | larch body-indent --sub
  fi

  if test "$archive/$version" = "$cl_version" ; then
    larch changelog --new-entry "$lvl,$tmpdir/,,cooked-log" "$archive/$version" > "$cl"
  else
    larch changelog "$cl_version" > "$cl"
  fi

  # where is the changelog in the prev tree?
  # 
  prev_changelog_file="`printf \"%s\\n\" \"$cl\" \
                        | join -o 2.2 -1 1 -2 1 - \"$tmpdir/,,mod-files-index-by-name\" \
                        | join -o 2.1 -1 1 -2 2 - \"$tmpdir/,,patch-set/orig-files-index\"`"

  if test -e "$tmpdir/,,patch-set/patches/$cl.patch" ; then
    set +e
    diff -u "$prev_tree/$prev_changelog_file" "$cl" >  "$tmpdir/,,patch-set/patches/$cl.patch"
    diff_stat=$?
    set -e
    if test $diff_stat -ne 0 -a $diff_stat -ne 1 ; then
      printf "commit: internal error updating %s\\n" "$cl.patch" 1>&2
      printf "  original file: %s\\n" "$prev_tree/$prev_changelog_file" 1>&2
      printf "  modified file: %s\\n" "$cl" 1>&2
      printf "  diff exit status: %d\\n" $diff_stat 1>&2
      printf "\\n"
      exit 1
    fi
  elif test -e "$tmpdir/,,patch-set/new-files-archive/$cl" ; then
    rm -f  "$tmpdir/,,patch-set/new-files-archive/$cl"
    cp -p "$cl"  "$tmpdir/,,patch-set/new-files-archive/$cl"
  else
    printf "commit: internal error\\n" 1>&2
    printf "  can't figure out how to modify patch set for %s\\n" "$cl"
    printf "\\n"
  fi
done

if test ! -z "$report" ; then
  if test ! -z "`head -n 1 \"$tmpdir/,,automated-changelogs-key\"`" ; then 
    printf "\\n" | larch body-indent --sub
  else
    printf "none\\n" | larch body-indent --sub
  fi
fi

# Conditions
#
#   $tmpdir/,,patch-set         -- patch set for new revision
#   $tmpdir/,,cooked-log        -- log file for new revision
#   $tmpdir/$version--$lvl      -- pristine tree of new revision
#
################################################################
# Construct the larch Template Dir
# 
#

if test ! -z "$report" ; then
  larch heading --sub "assembling the archive directory\\n"
  if test ! -z "$verbose" ; then
    larch heading --sub --sub "start time: %s\\n" "`date`"
  fi
fi

tmparch="$tmpdir/,,arch"
tmparch2="$tmpdir/arch"

mkdir "$tmparch"

mv "$tmpdir/,,patch-set" "$tmparch/$version--$lvl.patches"
cd "$tmparch"
tar -zcf "$version--$lvl.patches.tar.gz" "$version--$lvl.patches"
mv "$version--$lvl.patches" ../,,patch-set

cp "$tmpdir/,,cooked-log" "$tmparch/log"

cd "$tmparch"

larch make-lock revision-lock

if test $continuation -ne 0 ; then
  printf "%s\\n" "$continuation_archive/$continuation_branch--$continuation_vsn--$continuation_lvl" > CONTINUATION
fi

mv "$tmparch" "$tmparch2"

if test ! -z "$verbose" ; then
  larch heading --sub --sub "finish time: %s\\n" "`date`"
fi

# Conditions
# 
#   $tmparch2                   -- template of revision directory
#   $prev_tree                  -- points to previous revision
# 
# $tmpdir has these files:
#    
#    ,,wd-dirs          Various specialized file lists
#    ,,wd-files
#    ,,log-ls
#    
#    ,,prev-dirs
#    ,,prev-files
#    ,,prev-patches
#    
#    ,,wd-only-dirs     Various file list diffs and intersections
#    ,,prev-only-dirs
#    
#    ,,wd-only-files
#    ,,prev-only-files
#    ,,files-in-both
#    
#    ,,wd-only-patches
#    ,,wd-only-patches
#    
#    ,,modified-files   List of modified files
#    
#    ,,raw-log          Log as written by user
#    
#    ,,cooked-log       Log with headers added
#    
#    ,,patch-set        The pach-set tree
#    
#    arch               A template of the archive directory
# 
#    $version--$lvl     A copy of the project tree
# 
# 
# 
# 
################################################################
# Install the Arch Template Dir or Clean Up the Output Dir
# 
# 

status=0

if test -z "$dry_run" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub "installing the archive directory in the archive\\n"
    if test ! -z "$verbose" ; then
      larch heading --sub --sub "start time: %s\\n" "`date`"
    fi
  fi

  # where does this get stored in the archive?
  # 
  destdir="$category/$branch/$version/$lvl"

  wftp-home

  if ! larch putlast -e commit -R "$archroot" -A "$archive" \
                    lock-revision "$version--$lvl" \
                    "$tmparch2" "$destdir" \
                    wd-txn "$wdroot" "$archive" "$version--$lvl" "$tmparch2/log" \
  ; then

    if test ! -z "$quiet" ; then
      larch heading --sub --sub "ARCHIVE TRANSACTION FAILED\\n"
      larch heading --sub --sub "finish time: %s\\n" "`date`"
    fi

    status=1

  else

    if test ! -z "$verbose" ; then
      larch heading --sub --sub "finish time: %s\\n" "`date`"
    fi

    if larch my-notifier > /dev/null 2>&1 ; then
      notifier="`larch my-notifier`"
      ( cd "$notifier" ; \
        larch without-archive as-daemon $ARCH_LOG_OPTION --null-output larch notify "$notifier" "$archive/$version" )
    fi

    if test ! -z "$report" ; then
      larch heading --sub "updating local cache to include new revision\\n"
    fi

    cd "$tmpdir"
    mkdir ,,new-pristine
    cd ,,new-pristine


    cached_for_this_wd=`larch pristines --unlocked --dir "$wdroot" "$prev_archive/$prev_branch--$prev_vsn--$prev_lvl"`

    if test -z "$cached_for_this_wd" ; then

      if test ! -z "$report" ; then
        larch heading --sub --sub "building revision from scratch\\n"
      fi

      larch nested --sub \
        larch build-revision -A "$archive" \
                            --silent $quiet_opt $report_opt $verbose_opt $debug_opt \
                            --cache "$wdroot/.." \
                            "$prev_branch--$prev_vsn--$prev_lvl"

    else
      
      if test ! -z "$report" ; then
        larch heading --sub --sub "recycling cached pristine revision\\n"
      fi

      larch take-from-cache "$prev_archive/$prev_branch--$prev_vsn--$prev_lvl"

    fi

    larch dopatch $method --delete-removed --quiet $report_opt $verbose_opt $debug_opt "$tmpdir/,,patch-set" .

    cd "$tmpdir"
    larch wd-mv-pristine $archive $version--$lvl "$tmpdir/,,new-pristine" "$wdroot"

    cd "$wdroot"
    rm -rf "$tmpdir"
    rm -f "$logfile~"
    mv "$logfile" "$logfile~"

  fi

else

  cd "$tmpdir"

  mv ,,patch-set patch-set

  ln -s "$prev_tree" $prev_branch--$prev_vsn--$prev_lvl

  if test -z "$quiet" ; then
    larch heading --sub "output in %s\\n" "$tmpdir"
  fi

fi

if test ! -z "$quiet" ; then
  larch heading --sub "commit finish time: %s\\n" "`date`"
  larch heading --sub "commit exit status: %d\\n" "$status"
fi

exit $status

# tag: Tom Lord Thu Dec 13 16:04:36 2001 (archive-transactions/fast-commit.sh)
#
