# 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 -n 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 -n 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 -n 1`"

eval "$rvn2_archive_settings"
dir_rvn2_lvl="`larch log-ls $debug_opt --reverse \"$rvn2_archive/$rvn2_version\" | head -n 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
  printf "  To fix this, consider\\n" 1>&2
  printf "    larch join-branch --help\\n" 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
  printf "  To fix this, consider\\n" 1>&2
  printf "    larch join-branch --help\\n" 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 -n 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 -n 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 -n 1`"
else
  rvn1_rvn2_mergept="`larch merge-points $debug_opt --reverse \"$rvn1_archive/$rvn1_version--$rvn1_latest_lvl\" \"$rvn2_archive/$rvn2_version\" | head -n 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 -n 1`"
else
  rvn2_rvn1_mergept="`larch merge-points $debug_opt --reverse \"$rvn2_archive/$rvn2_version--$rvn2_latest_lvl\" \"$rvn1_archive/$rvn1_version\" | head -n 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)
#
