#!/bin/sh
# star-merge.sh: merge of mutually merged branches
# 
################################################################
# 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 "merge of mutually merged branches\\n"
                printf "usage: star-merge [options] A B C output-dir\\n"
                printf "usage: star-merge [options] --in-place A B C\\n"
                printf "usage: star-merge --finish [DIR]\\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 " --in-place                    star-merge in place\\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 " --finish                      finish a two-part merge\\n"
		printf "\\n"
                printf "A, B, and C should be the names of two revisions and a project tree,\\n"
                printf "in any order.  The project tree name must be unabmiguously a file name:\\n"
                printf "a handy trick is to prefix the name with \"./\".\\n"
                printf "\\n"
                printf "The tree must have patch logs for both revisions.  It must be up-to-date\\n"
                printf "with one revision and at the same patch level in the other version as the\\n"
                printf "the up-to-date revision.  The tree may have local modifications.\\n"
                printf "\\n"
		printf "The two revisions must have a common ancestor, but neither is required to have\\n"
                printf "a patch log for the other.\\n"
                printf "\\n"
                printf "This command performs a merge, resulting in a tree that is fully up-to-date\\n"
                printf "with both revisions and includes the local changes from the original tree.\\n"
                printf "\\n"
                printf "The order of arguments determines the precedence of changes from the two\\n"
		printf "lines of development and the project tree.  In other words, to build the\\n"
		printf "output directory, \"star-merge\" (logically) starts with the changes (since\\n"
		printf "the common ancestor) in A, then adds any missing changes from B, then adds any\\n"
		printf "missing changes from C.\\n"
                printf "\\n"
                printf "\"star-merge\" uses patch log information to avoid gratuitous conflicts.  The\\n"
		printf "actual merge technique used is determined by the patch histories of the\\n"
                printf "two revisions and is described as part of the output of \"star-merge\".\\n"
                printf "\\n"
                printf "In most cases, the output directory can be built by the application of a\\n"
                printf "single patch.  If the project tree is the third argument, two patches are\\n"
                printf "necessary, either of which may cause conflicts.  If conflicts occur during\\n"
                printf "the first patch of a two patch merge, \"star-merge\" stops leaving the output\\n"
                printf "directory in an intermediate state.  After fixing the conflicts, use the\\n"
                printf "\"--finish\" form of the command to apply the second patch.\\n"
                printf "\\n"
                exit 0
                ;;

      *)
                ;;
    esac
  done
fi

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

archroot=
archive=

in_place=
finish=

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

while test $# -ne 0 ; do

  case "$1" in 

    --in-place) shift
    		in_place=--in_place
		;;

    --finish)   shift
    		finish=--finish
		;;

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

    ----rvn1-archive-settings)		shift
    					if test $# -eq 0 ; then
                          		  printf "star-merge: ----rvn1-archive-settings requires an argument\\n" 1>&2
					  printf "try --help\\n" 1>&2
					  exit 1
					fi
    					rvn1_archive_settings="$1"
					shift
					;;
    ----rvn2-archive-settings)		shift
    					if test $# -eq 0 ; then
                          		  printf "star-merge: ----rvn2-archive-settings requires an argument\\n" 1>&2
					  printf "try --help\\n" 1>&2
					  exit 1
					fi
    					rvn2_archive_settings="$1"
					shift
					;;

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

    --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 "star-merge: debugging output enabled\\n"
    		set -x
		debug_opt=--debug
		;;


    -R|--root)          shift
                        if test $# -eq 0 ; then
                          printf "star-merge: -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 "star-merge: -A and --archive require an argument\\n" 1>&2
                          printf "try --help\\n" 1>&2
                          exit 1
                        fi
                        archive="$1"
                        shift
                        ;;

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

    *)                  break
                        ;;
  esac

done



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

if test ! -z "$finish" ; then

  if test $# -gt 1 ; then
    printf "usage: star-merge [options] --finish [dir]\\n" 1>&2
    printf "try --help\\n" 1>&2
    exit 1
  fi

  if test $# -gt 0 ; then
    dir="$1"
    shift
  else
    dir=.
  fi

else

  if test -z "$in_place" ; then
    n_dir_args=1
  else
    n_dir_args=0
  fi

  n_args=$((3 + $n_dir_args))

  if test $# -ne $n_args ; then
    printf "usage: star-merge [options] A B C output-dir\\n" 1>&2
    printf "       star-merge [options] --in-place A B C\\n" 1>&2
    printf "try --help\\n" 1>&2
    exit 1
  fi

  A="$1"
  shift

  B="$1"
  shift

  C="$1"
  shift

  if test $# -ne 0 ; then
    new_dir="$1"
    shift
  else
    new_dir=
  fi

fi

################################################################
# Handle --finish
# 

if test ! -z "$finish" ; then

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

  wdroot="`larch tree-root`"
  cd "$wdroot"
  mv "{arch}/++resolve-conflicts" "{arch}/,,resolve-conflicts"
  cmd="`cat {arch}/,,resolve-conflicts/star-merge`"
  set +e
  larch nested eval "$cmd"
  patch_stat=$?
  set -e
  rm -rf {arch}/,,resolve-conflicts
  exit "$patch_stat"
fi

################################################################
# Sanity Check and Process Defaults (not --finish)
# 
# The remainder of the script handles cases which are not --finish
# invocations.
# 

here="`pwd`"

abs_dir()
{
  cd "$here"
  cd "$1"
  pwd
}

if ! larch valid-package-name --tolerant -- "$A" ; then

  if test ! -e "$A" ; then
    larch valid-package-name -e star-merge --tolerant -- "$A"
  fi

  A="`abs_dir \"$A\"`"
  dir="$A"

  A_is_file=true
  file_is=A

  rvn1_is=B
  rvn2_is=C

  rvn1_spec="$B"
  rvn2_spec="$C"

  cd "$A"
  tree_version="`larch tree-version`"

else

  A_is_file=

fi

if ! larch valid-package-name --tolerant -- "$B" ; then

  if test ! -z "$A_is_file" || test ! -e "$B" ; then
    larch valid-package-name -e star-merge --tolerant -- "$B"
  fi

  B="`abs_dir \"$B\"`"
  dir="$B"

  B_is_file=true
  file_is=B

  rvn1_is=A
  rvn2_is=C

  rvn1_spec="$A"
  rvn2_spec="$C"

  cd "$B"
  tree_version="`larch tree-version`"

else

  B_is_file=

fi


if ! larch valid-package-name --tolerant -- "$C" ; then

  if test ! -z "$A_is_file" || test ! -z "$B_is_file" ; then
    larch valid-package-name -e star-merge --tolerant -- "$C"
  fi

  if test ! -e "$C" ; then
    printf "\\n" 1>&2
    printf "star-merge: no such directory\\n" 1>&2
    printf "  dir: %s\\n" "$C" 1>&2
    printf "\\n" 1>&2
    exit 1
  fi

  C="`abs_dir \"$C\"`"
  dir="$C"

  C_is_file=true
  file_is=C

  rvn1_is=A
  rvn2_is=B

  rvn1_spec="$A"
  rvn2_spec="$B"

  cd "$C"
  tree_version="`larch tree-version`"

else

  C_is_file=

fi

if test -z "$rvn1_spec" -a -z "$rvn2_spec" ; then
  printf "\\n" 1>&2
  printf "star-merge: no directory argument?\\n" 1>&2
  printf "  At least one argument must be a project tree.\\n" 1>&2
  printf "  If the directory name looks like a branch, version, or\\n" 1>&2
  printf "  revision name, prefix it with \"./\" to disambiguate.\\n" 1>&2
  printf "\\n" 1>&2
  exit 1
fi


rvn1_archive="`larch parse-package-name --arch -R \"$archroot\" -A \"$archive\" \"$rvn1_spec\"`"
rvn2_archive="`larch parse-package-name --arch -R \"$archroot\" -A \"$archive\" \"$rvn2_spec\"`"

rvn1_category="`larch parse-package-name --basename \"$rvn1_spec\"`"
rvn2_category="`larch parse-package-name --basename \"$rvn2_spec\"`"

rvn1_branch="`larch parse-package-name \"$rvn1_spec\"`"
rvn2_branch="`larch parse-package-name \"$rvn2_spec\"`"

if test -z "$in_place" ; then

  if test -e "$new_dir" ; then
    printf "\\n" 1>&2
    printf "star-merge: output directory already exists\\n" 1>&2
    printf "  dir: %s\\n" "$new_dir" 1>&2
    printf "\\n" 1>&2
  fi

  cd "$here"
  cd "`dirname \"$new_dir\"`"
  new_dir="`pwd`/`basename \"$new_dir\"`"

else

  new_dir="$dir"

fi

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

if test "(" -z "$__restart" -a ! -z "$quiet" ")" -o ! -z "$report" ; then
  larch heading "star-merge\\n"
  printf "arguments: %s\\n"  "$command_line" | fold -w 60 | larch body-indent
  larch heading --sub "star-merge start time: %s\\n" "`date`"
  larch heading --sub "A: %s\\n" "$A"
  larch heading --sub "B: %s\\n" "$B"
  larch heading --sub "C: %s\\n" "$C"
fi

################################################################
# Ensure That We Have Two Archive Connections 
# 

if test ! -z "$WITHARCHIVE" ; then

  if test -z "$rvn1_archive_settings" -a "(" "$WITHARCHIVE" = "$rvn1_archive" ")" ; then
    rvn1_archive_settings="`larch with-archive-settings`"
  fi

  if test -z "$rvn2_archive_settings" -a "(" "$WITHARCHIVE" = "$rvn2_archive" ")" ; then
    rvn2_archive_settings="`larch with-archive-settings`"
  fi

fi

if test -z "$rvn1_archive_settings" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub "restarting with connection to archive\\n"
    printf "%s\\n" "$rvn1_archive" | larch body-indent --sub
  fi

  if test -z "$in_place" ; then
    exec larch with-archive -A "$rvn1_archive" \
		  larch star-merge \
			$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
			----restart \
			----rvn1-archive-settings "$rvn1_archive_settings" \
			----rvn2-archive-settings "$rvn2_archive_settings" \
			-- "$A" "$B" "$C" "$new_dir"
  else
    exec larch with-archive -A "$rvn1_archive" \
		  larch star-merge \
			$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
			----restart \
			--in-place \
			----rvn1-archive-settings "$rvn1_archive_settings" \
			----rvn2-archive-settings "$rvn2_archive_settings" \
			-- "$A" "$B" "$C"
  fi
fi


if test -z "$rvn2_archive_settings" ; then

  if test ! -z "$quiet" ; then
    larch heading --sub "restarting with connection to archive\\n"
    printf "%s\\n" "$rvn1_archive" | larch body-indent --sub
  fi

  if test -z "$in_place" ; then
    exec larch with-archive -A "$rvn2_archive" \
		  larch star-merge \
			$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
			----restart \
			----rvn1-archive-settings "$rvn1_archive_settings" \
			----rvn2-archive-settings "$rvn2_archive_settings" \
			-- "$A" "$B" "$C" "$new_dir"
  else
    exec larch with-archive -A "$rvn2_archive" \
		  larch star-merge \
			$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
			----restart \
			--in-place \
			----rvn1-archive-settings "$rvn1_archive_settings" \
			----rvn2-archive-settings "$rvn2_archive_settings" \
			-- "$A" "$B" "$C"
  fi
fi

################################################################
# What Versions are We Merging?
# 

if larch valid-package-name --vsn --tolerant "$rvn1_spec" ; then
  rvn1_version="`larch parse-package-name --package-version \"$rvn1_spec\"`"
else
  eval "$rvn1_archive_settings"
  rvn1_version="`larch versions --reverse \"$rvn1_archive/$rvn1_spec\" | head -1`"
fi

if test ! -z "$quiet" ; then
  larch heading --sub "%s version: %s\\n" "$rvn1_is" "$rvn1_version"
fi

if larch valid-package-name --vsn --tolerant "$rvn2_spec" ; then
  rvn2_version="`larch parse-package-name --package-version \"$rvn2_spec\"`"
else
  eval "$rvn2_archive_settings"
  rvn2_version="`larch versions $debug_opt --reverse \"$rvn2_archive/$rvn2_spec\" | head -1`"
fi

if test ! -z "$quiet" ; then
  larch heading --sub "%s version: %s\\n" "$rvn2_is" "$rvn2_version"
fi

################################################################
# What Is the Latest Revision for Which Dir has a Patch?
# 

cd "$dir" 

eval "$rvn1_archive_settings"
dir_rvn1_lvl="`larch log-ls $debug_opt --reverse \"$rvn1_archive/$rvn1_version\" | head -1`"

eval "$rvn2_archive_settings"
dir_rvn2_lvl="`larch log-ls $debug_opt --reverse \"$rvn2_archive/$rvn2_version\" | head -1`"

if test -z "$dir_rvn1_lvl" ; then
  printf "star-merge: project tree not based on version\\n" 1>&2
  printf "  project tree: %s\\n" "$dir" 1>&2
  printf "  has no patch level for version:\\n" 1>&2
  printf "    %s\\n" "$rvn1_archive/$rvn1_version" 1>&2
  printf "\\n" 1>&2
  exit 1
fi

if test -z "$dir_rvn2_lvl" ; then
  printf "star-merge: project tree not based on version\\n" 1>&2
  printf "  project tree: %s\\n" "$dir" 1>&2
  printf "  has no patch level for version:\\n" 1>&2
  printf "    %s\\n" "$rvn2_archive/$rvn2_version" 1>&2
  printf "\\n" 1>&2
  exit 1
fi

################################################################
# What Revisions are we Merging With
# 

if larch valid-package-name --patch-level "$rvn1_spec" ; then
  rvn1_latest_lvl="`larch parse-package-name --lvl \"$rvn1_spec\"`"
else
  eval "$rvn1_archive_settings"
  rvn1_latest_lvl="`larch revisions $debug_opt --reverse \"$rvn1_archive/$rvn1_version\" | head -1`"

  if test -z "$rvn1_latest_lvl" ; then
    printf "star-merge: no revisions in version\\n" 1>&2
    printf "  version: %s/%s" "$rvn1_archive" "$rvn1_version" 1>&2
    printf "\\n"
    exit 1
  fi
fi

if larch valid-package-name --patch-level "$rvn2_spec" ; then
  rvn2_latest_lvl="`larch parse-package-name --lvl \"$rvn2_spec\"`"
else
  eval "$rvn2_archive_settings"
  rvn2_latest_lvl="`larch revisions $debug_opt --reverse \"$rvn2_archive/$rvn2_version\" | head -1`"

  if test -z "$rvn2_latest_lvl" ; then
    printf "star-merge: no revisions in version\\n" 1>&2
    printf "  version: %s/%s" "$rvn2_archive" "$rvn2_version" 1>&2
    printf "\\n"
    exit 1
  fi
fi

################################################################
# If Dir is Already Up-to-Date With Both, We're Done
# 

if test -z "$force" \
   -a "$rvn1_latest_lvl" = "$dir_rvn1_lvl" \
   -a "$rvn2_latest_lvl" = "$dir_rvn2_lvl" ; then
  if test ! -z "$quiet" ; then
    larch heading --sub "tree already up-to-date (use --force if necessary)\\n" 
    larch heading --sub "star-merge finish time: %s\\n" "`date`"
  fi
  exit 0
fi

################################################################
# If Dir is Not Up-to-Date With Either, We're Stuck
# 

if test "$rvn1_latest_lvl" != "$dir_rvn1_lvl" \
        -a "$rvn2_latest_lvl" != "$dir_rvn2_lvl" ; then

  if test ! -z "$quiet" ; then
    printf "\\n" 1>&2
    printf "tree not up-to-date with either revision\\n" 1>&2
    printf "\\n" 1>&2
  fi
  exit 1

fi

################################################################
# How Up-to-date is Each Revision With the Other?
# 

if test ! -z "$quiet" ; then
  larch heading --sub "computing merge points\\n"
fi

if test "$rvn1_latest_lvl" = "$dir_rvn1_lvl" ; then
  rvn1_rvn2_mergept="`larch merge-points $debug_opt --reverse --dir \"$dir\" \"$rvn1_archive/$rvn1_version--$dir_rvn1_lvl\" \"$rvn2_archive/$rvn2_version\" | head -1`"
else
  rvn1_rvn2_mergept="`larch merge-points $debug_opt --reverse \"$rvn1_archive/$rvn1_version--$rvn1_latest_lvl\" \"$rvn2_archive/$rvn2_version\" | head -1`"
fi

rvn1_rvn2_lvl=${rvn1_rvn2_mergept#*	}
rvn1_rvn2_mergept=${rvn1_rvn2_mergept%	*}

if test ! -z "$quiet" ; then
  if test ! -z "$rvn1_rvn2_mergept" ; then
    larch heading --sub --sub "B %s merged into A at %s\\n" "$rvn1_rvn2_lvl" "$rvn1_rvn2_mergept"
  else
    larch heading --sub --sub "B has never been merged into A\\n" 
  fi
fi


if test "$rvn2_latest_lvl" = "$dir_rvn2_lvl" ; then
  rvn2_rvn1_mergept="`larch merge-points $debug_opt --reverse --dir \"$dir\" \"$rvn2_archive/$rvn2_version--$dir_rvn2_lvl\" \"$rvn1_archive/$rvn1_version\" | head -1`"
else
  rvn2_rvn1_mergept="`larch merge-points $debug_opt --reverse \"$rvn2_archive/$rvn2_version--$rvn2_latest_lvl\" \"$rvn1_archive/$rvn1_version\" | head -1`"
fi

rvn2_rvn1_lvl=${rvn2_rvn1_mergept#*	}
rvn2_rvn1_mergept=${rvn2_rvn1_mergept%	*}

if test ! -z "$quiet" ; then
  if test ! -z "$rvn2_rvn1_mergept" ; then
    larch heading --sub --sub "A %s merged into B at %s\\n" "$rvn2_rvn1_lvl" "$rvn2_rvn1_mergept"
  else
    larch heading --sub --sub "A has never been merged into B\\n" 
  fi
fi


################################################################
# Which Revision is More Merged With the Other?
# 
# 

if test    -z "$rvn1_rvn2_lvl" \
        -a -z "$rvn2_rvn1_lvl" ; then

  printf "\\n" 1>&2
  printf "star-merge: neither revision has ever been merged with the other\\n" 1>&2
  printf "  nor is either a branch from the other.  Therefore, these are\\n" 1>&2
  printf "  not star-topology branches.\\n" 1>&2
  printf "\\n" 1>&2
  exit 1
fi

if   test -z "$rvn1_rvn2_lvl" ; then

  more_merged=rvn2

elif test -z "$rvn2_rvn1_lvl" ; then

  more_merged=rvn1

elif larch patch-level-lt "$rvn2_rvn1_lvl" "$rvn1_rvn2_mergept" ; then

  more_merged=rvn1

else

  more_merged=rvn2

fi

###############################################################
# Normalize Names
# 
# AA - the less merged revision
# BB - the more merged revision
# 
# order - perm(AA,BB,WD)
# 
# 

if test "$more_merged" = "rvn2" ; then

  AA="$rvn1_is"
  BB="$rvn2_is"
  eval "${rvn1_is}_normalized=AA"
  eval "${rvn2_is}_normalized=BB"

  AA_archive="$rvn1_archive"
  BB_archive="$rvn2_archive"

  AA_category="$rvn1_category"
  BB_category="$rvn2_category"

  AA_branch="$rvn1_branch"
  BB_branch="$rvn2_branch"

  AA_version="$rvn1_version"
  BB_version="$rvn2_version"

  AA_lvl="$rvn1_latest_lvl"
  BB_lvl="$rvn2_latest_lvl"

  AA_BB_lvl="$rvn1_rvn2_lvl"
  BB_AA_lvl="$rvn2_rvn1_lvl"

  dir_AA_lvl="$dir_rvn1_lvl"
  dir_BB_lvl="$dir_rvn2_lvl"

  AA_archive_settings="$rvn1_archive_settings"
  BB_archive_settings="$rvn2_archive_settings"

else

  AA="$rvn2_is"
  BB="$rvn1_is"
  eval "${rvn1_is}_normalized=BB"
  eval "${rvn2_is}_normalized=AA"

  AA_archive="$rvn2_archive"
  BB_archive="$rvn1_archive"

  AA_category="$rvn2_category"
  BB_category="$rvn1_category"

  AA_branch="$rvn2_branch"
  BB_branch="$rvn1_branch"

  AA_version="$rvn2_version"
  BB_version="$rvn1_version"

  AA_lvl="$rvn2_latest_lvl"
  BB_lvl="$rvn1_latest_lvl"

  AA_BB_lvl="$rvn2_rvn1_lvl"
  BB_AA_lvl="$rvn1_rvn2_lvl"

  dir_AA_lvl="$dir_rvn2_lvl"
  dir_BB_lvl="$dir_rvn1_lvl"

  AA_archive_settings="$rvn2_archive_settings"
  BB_archive_settings="$rvn1_archive_settings"

fi

eval "${file_is}_normalized=WD"

permutation=${A_normalized}${B_normalized}${C_normalized}


################################################################
# Make Sure the Tree is at the Right Levels and Find Out Which 
# it is in Sync With

if test "$dir_AA_lvl" = "$AA_lvl" ; then

  dir_current=AA

  if test "$dir_BB_lvl" != "$AA_BB_lvl" ; then
    printf "\\n" 1>&2
    printf "star-merge: illegal merge\\n" 1>&2
    printf "\\n" 1>&2
    printf "  The project tree must be up-to-date with respect to one of the\\n" 1>&2
    printf "  two branches (which it is).  Call this the \"up-to-date branch\".\\n" 1>&2
    printf "  The project tree and the up-to-date branch are both at some\\n" 1>&2
    printf "  patch level in the other branch (the \"out-of-date branch\").\\n" 1>&2
    printf "  The restriction is that both the tree and the up-to-date branch\\n" 1>&2
    printf "  must be at the *same* patch level of the out-of-date branch.\\n" 1>&2
    printf "  In this case, they are not.\\n" 1>&2
    printf "\\n" 1>&2
    printf "  Usually, the way to fix this error is to \"commit\" the project tree\\n" 1>&2
    printf "  to the up-to-date branch before attempting the merge.\\n" 1>&2
    printf "\\n" 1>&2
    printf "    tree: %s\\n" "$dir" 1>&2
    printf "    up-to-date branch: %s\\n" "$AA"1>&2
    printf "    out-of-date branch: %s\\n" "$BB"1>&2
    printf "\\n" 1>&2
    printf "    tree patch level on out-of-date branch: %s\\n" "$dir_BB_lvl" 1>&2
    printf "    up-to-date branch patch level on out-of-date branch: %s\\n" "$AA_BB_lvl" 1>&2
    printf "\\n" 1>&2
    exit 1
  fi

else

  dir_current=BB

  if test "$dir_AA_lvl" != "$BB_AA_lvl" ; then
    printf "\\n" 1>&2
    printf "star-merge: illegal merge\\n" 1>&2
    printf "\\n" 1>&2
    printf "  The project tree must be up-to-date with respect to one of the\\n" 1>&2
    printf "  two branches (which it is).  Call this the \"up-to-date branch\".\\n" 1>&2
    printf "  The project tree and the up-to-date branch are both at some\\n" 1>&2
    printf "  patch level in the other branch (the \"out-of-date branch\").\\n" 1>&2
    printf "  The restriction is that both the tree and the up-to-date branch\\n" 1>&2
    printf "  must be at the *same* patch level of the out-of-date branch.\\n" 1>&2
    printf "  In this case, they are not.\\n" 1>&2
    printf "\\n" 1>&2
    printf "  Usually, the way to fix this error is to \"commit\" the project tree\\n" 1>&2
    printf "  to the up-to-date branch before attempting the merge.\\n" 1>&2
    printf "\\n" 1>&2
    printf "    tree: %s\\n" "$dir" 1>&2
    printf "    up-to-date branch: %s\\n" "$BB"1>&2
    printf "    out-of-date branch: %s\\n" "$AA"1>&2
    printf "\\n" 1>&2
    printf "    tree patch level on out-of-date branch: %s\\n" "$dir_AA_lvl" 1>&2
    printf "    up-to-date branch patch level on out-of-date branch: %s\\n" "$BB_AA_lvl" 1>&2
    printf "\\n" 1>&2
    exit 1
  fi

fi

################################################################
# Do the Merge
# 

mv_dir_for_in_place()
{
  dir_base="`basename \"$dir\"`"
  dir_parent="`dirname \"$dir\"`"
  cd "$dir_parent"
  saved_dir_base="++saved-$dir_base.`date +%Y-%m-%d`.$$.bak"
  mv "$dir_base" "$saved_dir_base"
  dir="$dir_parent/$saved_dir_base"
  printf "%s\\n" "$dir"
}

if test "$dir_current" = AA ; then

  case $permutation in 

    AAWDBB|WDAABB|WDBBAA)		if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n" "$BB_archive/$BB_version--$BB_lvl" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$dir" | larch body-indent --sub
					fi

					if test ! -z "$in_place" ; then
    					  set +e
					  larch delta-patch --in-place \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$BB_archive/$BB_version--$BB_lvl" \
							"$dir"
					  patch_stat=$?
					  set -e
					  cd "$dir"
					  larch set-tree-version "$tree_version"
					else
					  set +e
					  larch delta-patch \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$BB_archive/$BB_version--$BB_lvl" \
							"$dir" \
							"$new_dir"
					  patch_stat=$?
					  set -e
					  cd "$new_dir"
					  larch set-tree-version "$tree_version"
					fi
    					;;

    AABBWD)				if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "merge in two steps\\n" | larch body-indent --nonl --sub 
					  printf "\\n" | larch body-indent --sub
					  printf "tmp = delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n"  "$BB_archive/$BB_version--$BB_lvl" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$AA_archive/$AA_version--$AA_lvl" | larch body-indent --nonl --sub
					  printf "\\n" | larch body-indent --sub
					  printf "answer = delta(L,M)[tmp]\\n" | larch body-indent --sub
					  printf "  where L = %s\\n" "$AA_archive/$AA_version--$AA_lvl" | larch body-indent --nonl --sub
					  printf "        M = %s\\n" "$dir" | larch body-indent --sub
					fi
					if test -z "$in_place" ; then
					  final_dir="$new_dir"
					else
    					  final_dir="$dir"
					  dir="`mv_dir_for_in_place`"
					fi

					eval "$AA_archive_settings"
					larch get $debug_opt -- "$AA_archive/$AA_version--$AA_lvl" "$final_dir"
					cd "$final_dir"
					larch set-tree-version "$tree_version"

					cd "$final_dir/{arch}"
					mkdir ./,,resolve-conflicts
					cd ,,resolve-conflicts
					printf "larch delta-patch --in-place \\\\\\n" > star-merge
					printf "\\t%s -- \\\\\\n" "$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt" >> star-merge
					printf "\\t\"%s\"\\\\\\n\\t\"%s\"\\\\\\n\\t\"%s\"\\n" \
					        "$AA_archive/$AA_version--$AA_lvl" \
						"$dir" \
						"$final_dir" \
					>> star-merge
					printf "larch star-merge --finish '%s'\\n" "$final_dir" > cmd


					set +e
					larch delta-patch --in-place  \
						$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
						-- \
						"$AA_archive/$AA_version--$BB_AA_lvl" \
						"$BB_archive/$BB_version--$BB_lvl" \
						"$final_dir"
					patch_stat=$?
					set -e

					if test $patch_stat = 0 ; then
					  cd "$final_dir"
					  cmd="`cat {arch}/,,resolve-conflicts/star-merge`"
					  set +e
					  larch nested eval "$cmd"
					  patch_stat=$?
					  set -e
					  rm -rf {arch}/,,resolve-conflicts
					else
					  cd "$final_dir/{arch}"
					  mv ,,resolve-conflicts ++resolve-conflicts
					fi
					;;

    BBAAWD|BBWDAA)			if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n" "$dir" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$BB_archive/$BB_version--$BB_lvl" | larch body-indent --sub
					fi
					if test ! -z "$in_place" ; then

					  final_dir="$dir"
					  dir="`mv_dir_for_in_place`"
					  larch get $debug_opt -- "$BB_archive/$BB_version--$BB_lvl" "$final_dir"

					  set +e
					  larch delta-patch --in-place \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$dir" \
							"$final_dir"
					  patch_stat=$?
					  set -e
					  cd "$final_dir"
					  larch set-tree-version "$tree_version"
					else
					  set +e
					  larch delta-patch \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$dir" \
							"$BB_archive/$BB_version--$BB_lvl" \
							"$new_dir"
					  patch_stat=$?
					  set -e
					  cd "$new_dir"
					  larch set-tree-version "$tree_version"
					fi
    					;;
  esac

else

  case $permutation in 

    BBWDAA|WDBBAA|WDAABB)		if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n" "$AA_archive/$AA_version--$AA_lvl" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$dir" | larch body-indent --sub
					fi
					if test ! -z "$in_place" ; then
					  set +e
					  larch delta-patch --in-place \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$AA_archive/$AA_version--$AA_lvl" \
							"$dir"
					  patch_stat=$?
					  set -e
					  cd "$dir"
					  larch set-tree-version "$tree_version"
					else
					  set +e
					  larch delta-patch \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							-- \
					  	   	"$AA_archive/$AA_version--$BB_AA_lvl" \
							"$AA_archive/$AA_version--$AA_lvl" \
							"$dir" \
							"$new_dir"
					  patch_stat=$?
					  set -e
					  cd "$new_dir"
					  larch set-tree-version "$tree_version"
					fi
    					;;

    BBAAWD)				if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "merge in two steps\\n" | larch body-indent --nonl --sub 
					  printf "\\n" | larch body-indent --sub
					  printf "tmp = delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n"  "$AA_archive/$AA_version--$AA_lvl" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$BB_archive/$BB_version--$BB_lvl" | larch body-indent --nonl --sub
					  printf "\\n" | larch body-indent --sub
					  printf "answer = delta(L,M)[tmp]\\n" | larch body-indent --sub
					  printf "  where L = %s\\n" "$BB_archive/$BB_version--$BB_lvl" | larch body-indent --nonl --sub
					  printf "        M = %s\\n" "$dir" | larch body-indent --sub
					fi
					if test -z "$in_place" ; then
					  final_dir="$new_dir"
					else
    					  final_dir="$dir"
					  dir="`mv_dir_for_in_place`"
					fi

					eval "$BB_archive_settings"
					larch get $debug_opt -- "$BB_archive/$BB_version--$BB_lvl" "$final_dir"
					cd "$final_dir"
					larch set-tree-version "$tree_version"


					cd "$final_dir/{arch}"
					mkdir ./,,resolve-conflicts
					cd ,,resolve-conflicts
					printf "larch delta-patch --in-place \\\\\\n" > star-merge
					printf "\\t%s -- \\\\\\n" "$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt" >> star-merge
					printf "\\t\"%s\"\\\\\\n\\t\"%s\"\\\\\\n\\t\"%s\"\\n" \
					        "$BB_archive/$BB_version--$BB_lvl" \
						"$dir" \
						"$final_dir" \
					>> star-merge
					printf "larch star-merge --finish '%s'\\n" "$final_dir" > cmd

					set +e
					larch delta-patch --in-place  \
						$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
						-- \
						"$AA_archive/$AA_version--$BB_AA_lvl" \
						"$AA_archive/$AA_version--$AA_lvl" \
						"$final_dir"
					patch_stat=$?
					set -e

					if test $patch_stat = 0 ; then
					  cd "$final_dir"
					  cmd="`cat {arch}/,,resolve-conflicts/star-merge`"
					  set +e
					  larch nested eval "$cmd"
					  patch_stat=$?
					  set -e
					  rm -rf {arch}/,,resolve-conflicts
					else
					  cd "$final_dir/{arch}"
					  mv ,,resolve-conflicts ++resolve-conflicts
					fi
					;;

    AABBWD|AAWDBB)			if test ! -z "$quiet" ; then
					  larch heading --sub "patch strategy\\n"
					  printf "delta(X, Y)[Z]\\n" | larch body-indent --nonl --sub
					  printf "  where X = %s\\n" "$AA_archive/$AA_version--$BB_AA_lvl" | larch body-indent --nonl --sub
					  printf "        Y = %s\\n" "$dir" | larch body-indent --nonl --sub
					  printf "        Z = %s\\n" "$AA_archive/$AA_version--$AA_lvl" | larch body-indent --nonl body-indent --sub
					  printf "\\n" | larch body-indent --nonl --sub
					  printf "(this is the same as \"larch update Y Z\") \\n" | larch body-indent --sub
					fi
					if test ! -z "$in_place" ; then
					  set +e
					  larch update --in-place \
							$silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
							"$dir"\
							"$AA_archive/$AA_version--$AA_lvl"
					  patch_stat=$?
					  set -e
					  cd "$dir"
					  larch set-tree-version "$tree_version"
					else
					  set +e
					  larch update "$dir" \
						       $silent_opt $quiet_opt $report_opt $verbose_opt $debug_opt \
						      "$new_dir" \
						      "$AA_archive/$AA_version--$AA_lvl"
					  patch_stat=$?
					  set -e
					  cd "$new_dir"
					  larch set-tree-version "$tree_version"
					fi
    					;;
  esac

fi

if test $patch_stat -ne 0 -a $patch_stat -ne 1 ; then
  printf "\\n" 1>&2
  printf "star-merge: patch operation failed with status %s\\n" "$patch_stat" 1>&2
  printf "\\n" 1>&2
  exit 2
fi

###############################################################
# Bye
# 
if test $patch_stat -eq 1 -a ! -z "$quiet" ; then
  larch heading --sub "NOTICE: merge conflicts occured\\n"
  printf "  Look for \".rej\" files in %s\\n" "`basename \"$new_dir\"`" | larch body-indent --sub
fi

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

exit "$patch_stat"

# tag: Tom Lord Fri Jan  4 20:12:58 2002 (branching-and-merging/star-merge.sh)
#
