# file      : tests/release.testscript
# license   : MIT; see accompanying LICENSE file

.include common.testscript project.testscript

# bdep-release requirements for the minimum supported git version are higher
# then the default 2.1.0 (see bdep/release.cxx for details).
#
+if! ($git_version_major >  2 || \
      $git_version_major == 2 && $git_version_minor >= 11)
  exit
end

g = [cmdline] git 2>! >&2

# Create the remote repository.
#
+mkdir --no-cleanup prj.git
+$g -C prj.git -c init.defaultBranch=master init --bare &prj.git/***

clone_rep      = [cmdline] cp --no-cleanup -pr ../prj.git ./ &prj.git/***
clone_root_rep = [cmdline] cp --no-cleanup -pr $~/prj.git ./ &prj.git/***

# Prepare the local repository.
#
# Set the origin repository location as a relative path, so we can copy the
# local/remote repository pair into the testscript scopes.
#
gp  = [cmdline] $g -C prj
log = [cmdline] $gp log '--pretty=format:"%d %s"'

+$gp config user.name 'Test Script'
+$gp config user.email 'testscript@example.com'
+$gp remote add origin ../prj.git
+$gp add '*'
+$gp commit -m 'Create' --no-verify
+$gp push --set-upstream origin master

clone_prj      = [cmdline] cp --no-cleanup -pr ../prj ./ &prj/***
clone_root_prj = [cmdline] cp --no-cleanup -pr $~/prj ./ &prj/***

clone_repos      = [cmdline] $clone_prj && $clone_rep
clone_root_repos = [cmdline] $clone_root_prj && $clone_root_rep

release += 2>!

# As a common approach we will be using a second local repository to validate
# the result of the release operation(s), that is normally pushed to the
# remote repository.
#
gp2    = [cmdline] $g -C prj2
clone2 = [cmdline] $g clone prj.git prj2 &prj2/***
pull2  = [cmdline] $gp2 pull --ff-only
fetch2 = [cmdline] $gp2 fetch
log2   = [cmdline] $gp2 log '--pretty=format:"%d %s"'

new += --vcs git,branch=master 2>-

: single-pkg
:
{
  : release
  :
  {
    test.arguments += --yes

    : version
    :
    {
      test.arguments += --push

      : patch
      :
      {
        $clone_root_repos;

        $* 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        cat prj2/manifest >>~%EOO%
          %.*
          name: prj
          version: 0.2.0-a.0.z
          language: c++
          %.*
          EOO
      }

      : alpha
      :
      {
        $clone_root_repos;

        $* --alpha 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0-a.1
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-a.1.z%d
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO
      }

      : beta
      :
      {
        $clone_root_repos;

        $* --beta 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0-b.1
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d
           (tag: v0.1.0-b.1) Release version 0.1.0-b.1
           Create
          EOO
      }

      : minor
      :
      {
        $clone_root_repos;

        $* --minor 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.2.0
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.3.0-a.0.z%d
           (tag: v0.2.0) Release version 0.2.0
           Create
          EOO
      }

      : major
      :
      {
        $clone_root_repos;

        $* --major 2>>~%EOE%;
          %.+
          pushing branch master, tag v1.0.0
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 1.1.0-a.0.z%d
           (tag: v1.0.0) Release version 1.0.0
           Create
          EOO
      }
    }

    : open
    :
    {
      test.arguments += --push

      : beta
      :
      {
        $clone_root_repos;

        $* --open-beta 2>'error: --open-beta specified for final current version 0.1.0' != 0;

        $* --alpha --open-beta 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0-a.1
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.0.z%d
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO

        $* --beta --open-beta 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0-b.1
          EOE

        $pull2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.0-b.1.z%d
           (tag: v0.1.0-b.1) Release version 0.1.0-b.1
           Change version to 0.1.0-b.0.z
           (tag: v0.1.0-a.1) Release version 0.1.0-a.1
           Create
          EOO
      }

      : patch
      :
      {
        $clone_root_repos;

        $* --open-patch 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Change version to 0.1.1-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        # Note: --open-patch is implied for bugfix release series.
        #
        $* 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.1
          EOE

        $pull2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.1.2-a.0.z%d
           (tag: v0.1.1) Release version 0.1.1
          % Change version to 0.1.1-a.0.z%
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : minor
      :
      {
        $clone_root_repos;

        $* --alpha --open-minor 2>'error: --open-minor specified for alpha current version 0.1.0-a.1' != 0;
        $* --beta  --open-minor 2>'error: --open-minor specified for beta current version 0.1.0-b.1' != 0;

        $* --open-patch 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $* 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.1
          EOE

        $* --open-minor 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.2
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.2) Release version 0.1.2
           Change version to 0.1.2-a.0.z
           (tag: v0.1.1) Release version 0.1.1
           Change version to 0.1.1-a.0.z
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : major
      :
      {
        $clone_root_repos;

        $* --open-major 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 1.0.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : base
      :
      {
        $clone_root_repos;

        $* --open-base 1.0 2>>EOE != 0;
          error: invalid base version '1.0': '.' expected after minor version
          EOE

        $* --open-base 1.0.0-a.1 2>>EOE != 0;
          error: invalid base version '1.0.0-a.1': pre-release
          EOE

        $* --open-base 1.2.3 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Change version to 1.2.3-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $* --open-base 1.2.2 2>>EOE != 0;
          error: base version 1.2.2 is less than or equal to current version 1.2.3
          EOE

        $* --open-base 1.2.3 2>>EOE != 0
          error: base version 1.2.3 is less than or equal to current version 1.2.3
          EOE
      }
    }

    : no
    :
    {
      test.arguments += --push

      : open
      :
      {
        $clone_root_repos;

        $* --no-open 2>>~%EOE%;
          %.+
          pushing branch master, tag v0.1.0
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --open 2>>~%EOE%;
          %.+
          pushing branch master
          EOE

        $pull2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : tag
      :
      {
        $clone_root_repos;

        $* --no-tag 2>>~%EOE%;
          %.+
          pushing branch master
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           Release version 0.1.0
           Create
          EOO

        $* --tag 2>>EOE != 0;
          error: current version 0.2.0-a.0.z is a snapshot
            info: use --force=snapshot to tag anyway
          EOE

        # While at it, verify that the --current-tag is forbidden for a
        # non-revision release.
        #
        $* --tag --force=snapshot --current-tag=update 2>>EOE != 0;
          error: --current-tag specified for non-revision current version 0.2.0-a.0.z
          EOE

        $* --tag --force=snapshot 2>>~%EOE%;
          %pushing tag v0.2.0-a.0.\.+%d
          EOE

        $pull2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.2.0-a.0.\.*.\.*\) Change version to 0.2.0-a.0.z%d
           Release version 0.1.0
           Create
          EOO
      }

      : tag-open
      :
      {
        $clone_root_repos;

        $* --no-tag --no-open 2>>~%EOE%;
          %.+
          pushing branch master
          EOE

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --tag 2>>EOE;
          pushing tag v0.1.0
          EOE

        $pull2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0%d
           Create
          EOO

        $* --open 2>>~%EOE%;
          %.+
          pushing branch master
          EOE

        $pull2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : commit
      :
      {
        $clone_root_repos;

        $* --no-commit 2>'error: both --push and --no-commit specified' != 0
      }
    }

    : validate-manifest
    {
      test.options += -q
      test.arguments += --push

      : file-value
      :
      {
        $clone_root_repos;

        rm prj/README.md;
        $gp commit -a -m 'Remove description';

        $* 2>>~%EOE% != 0;
          %error: unable to read .+README\.md referenced by description-file manifest value in .+manifest: .+%
          EOE

        touch prj/README.md;
        $gp add README.md;
        $gp commit -m 'Add README.md file';

        $* 2>>~%EOE% != 0;
          %error: description-file manifest value in .+manifest references empty file .+README.md%
          EOE

        echo '.' >= prj/README.md;
        $gp commit -a -m 'Fill README.md file';

        $*
      }

      : unknown-value
      :
      {
        $clone_root_repos;

        echo 'some-file: README' >+ prj/manifest;
        $gp commit -a -m 'Add file';

        $* 2>>~%EOE% != 0
          %error: invalid package manifest: .+manifest:.+:1: unknown name 'some-file' in package manifest%
          EOE
      }
    }

    : unstaged
    :
    {
      $clone_root_repos;
      echo '' >+ prj/manifest;

      $* 2>>EOE != 0;
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --no-commit;

      $gp commit -a -m 'Release version';

      $* --tag --push 2>>EOE;
        pushing branch master, tag v0.1.0
        EOE

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0, \.*\) Release version%d
         Create
        EOO
    }
  }

  : revision
  :
  {
    test.options += -q

    +$clone_root_repos
    +$* --no-open --push --yes

    test.arguments += --revision

    : current-tag
    :
    {
      +$clone_repos

      +echo '' >+ prj/manifest
      +$gp add manifest

      test.arguments += --push --no-edit

      : update
      :
      {
        $clone_repos;
        sp=' ';

        $* --current-tag=update <'y' 2>>~%EOE%;
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0 (update)
            push:    origin/master
          %continue\? \[y/n\] Updated tag 'v0.1.0' \.+%d
          EOE

        test.arguments += --yes;

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $* --current-tag=update --force=unchanged 2>>~%EOE%;
          %Updated tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO

        # Updates the existing local tag.
        #
        # Note that starting git 2.20.0 --force is also required (see git
        # release notes for details).
        #
        $pull2 --tags --force;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO
      }

      : remove
      :
      {
        $clone_repos;

        $* --current-tag=remove <'y' 2>>~%EOE%;
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0+1 (remove v0.1.0)
            push:    origin/master
          %continue\? \[y/n\] Deleted tag 'v0.1.0' \.+%d
          EOE

        test.arguments += --yes;

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $* --current-tag=remove --force=unchanged 2>>~%EOE%;
          %Deleted tag 'v0.1.0\+1' \.+%d
          EOE

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           Release version 0.1.0+1
           Release version 0.1.0
           Create
          EOO
      }

      : keep
      :
      {
        $clone_repos;
        sp = ' ';

        $* --current-tag=keep <'y' 2>>:"EOE";
          revising:
            package: prj
            current: 0.1.0
            release: 0.1.0+1
            commit:  yes
            tag:     v0.1.0+1
            push:    origin/master
          continue? [y/n]$sp
          EOE

        test.arguments += --yes;

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $* --current-tag=keep --force=unchanged;

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           (tag: v0.1.0+1) Release version 0.1.0+1
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+2, \.*\) Release version 0.1.0\+2%d
           (tag: v0.1.0+1) Release version 0.1.0+1
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }

    : tag
    :
    {
      +$clone_repos

      test.arguments += --push --no-edit --force=unchanged --yes

      +$* --no-tag

      +$clone2
      +$log2 >>:~%EOO%
        % \(HEAD -> master, origin/master, \.*\) Release version 0.1.0\+1%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      tag = [cmdline] $release --tag --push -q --yes -d prj

      : update
      :
      {
        $clone_repos;

        $tag --current-tag=update 2>>~%EOE%;
          %Updated tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO
      }

      : remove
      :
      {
        $clone_repos;

        $tag --current-tag=remove 2>>~%EOE%;
          %Deleted tag 'v0.1.0' \.+%d
          EOE

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Release version 0.1.0
           Create
          EOO
      }

      : keep
      :
      {
        $clone_repos;

        $tag --current-tag=keep;

        $log >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }

    : changes-staged
    :
    {
      test.arguments += --push --no-edit --yes

      +$clone_repos

      +echo '' >+ prj/manifest
      +$gp add manifest

      : normal
      :
      {
        $clone_repos;

        $*;

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }

      : stub
      :
      {
        $clone_root_repos;

        sed -i -e 's/^(version:) .*$/\1 0/' prj/manifest;
        $gp add manifest;

        $* --revision;

        $clone2;
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0\+1, \.*\) Release version 0\+1%d
           Create
          EOO
      }

      : no-tag
      :
      {
        $clone_repos;

        $* --no-tag;

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $release --tag --push --yes -d prj;

        $pull2 --tags --force; # Updates the existing local tag (see above).
        $log2 >>:~%EOO%
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO
      }
    }

    : no-changes-staged
    :
    {
      test.arguments += --push --yes

      $clone_repos;

      $* 2>>~%EOE% != 0;
        error: project directory has no staged changes
          info: revision increment must be committed together with associated changes
          info: use --force=unchanged to release anyway
        EOE

      $* --force=unchanged;

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : unstaged
    :
    {
      test.arguments += --yes;

      $clone_repos;

      echo '' >+ prj/manifest;

      $* 2>>EOE != 0;
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --no-commit;

      $gp commit -a -m 'Release revision';
      $release --tag --push --yes -d prj;

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release revision%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : amend
    :
    {
      test.arguments += --push --amend --no-edit --yes

      +$clone_repos

      +echo '' >+ prj/repositories.manifest
      +$gp commit -a -m 'Fix repositories.manifest' --no-verify

      +echo '' >+ prj/buildfile
      +$gp commit -a -m 'Fix buildfile' -m "Add '\n' to the end of the file." \
           --no-verify

      +echo '' >+ prj/manifest
      +$gp add manifest

      : no-squash
      :
      {
        $clone_repos;

        $*;

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Fix repositories.manifest
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }

      : squash
      :
      {
        $clone_repos;

        $* --squash 2;

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO

        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }

      : no-changes-staged
      :
      {
        $clone_repos;

        $gp commit -a -m 'Change manifest' --no-verify;

        $*;

        $clone2;
        $log2 >>:~%EOO%;
          % \(HEAD -> master, tag: v0.1.0\+1, \.*\) Release version 0.1.0\+1%d
           Fix buildfile
           Fix repositories.manifest
           (tag: v0.1.0) Release version 0.1.0
           Create
          EOO


        $log2 '--pretty=format:%B' >>EOO
          Release version 0.1.0+1

          Change manifest

          Fix buildfile

          Add '\n' to the end of the file.

          Fix repositories.manifest

          Release version 0.1.0

          Create
          EOO
      }
    }
  }

  : open
  :
  {
    test.options   += -q
    test.arguments += --yes

    : unstaged
    :
    {
      $clone_root_repos;

      $* --no-open;

      echo '' >+ prj/manifest;

      $* --open 2>>EOE != 0;
        error: project directory has unstaged changes
          info: run 'git status' for details
          info: use 'git add' to add the changes to this commit
          info: use 'git stash' to temporarily hide the changes
        EOE

      $* --open --no-commit;

      $gp commit -a -m 'Open dev cycle';
      $gp push origin HEAD --tags;

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Open dev cycle%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }
  }

  : no-commit
  :
  {
    test.arguments += --yes

    $clone_root_repos;

    $* --no-commit;

    $clone2;
    $log2 >>:~%EOO%;
      % \(HEAD -> master, .*\) Create%
      EOO

    $gp commit -a -m 'Release version';

    $* --tag;
    $* --open --no-commit;

    $gp commit -a -m 'Change version';
    $gp push origin HEAD --tags;

    $pull2;
    $log2 >>:~%EOO%
      % \(HEAD -> master, .*\) Change version%
       (tag: v0.1.0) Release version
       Create
      EOO
  }

  : no-progress
  :
  {
    test.arguments += --yes

    $clone_root_repos;

    $* --push --no-progress 2>>~%EOE%;
      %\.+ Release version 0.1.0%d
       1 file changed, 1 insertion(+), 1 deletion(-)
      %\.+ Change version to 0.2.0-a.0.z%d
       1 file changed, 1 insertion(+), 1 deletion(-)
      EOE

    $clone2;
    $log2 >>:~%EOO%
      % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
       (tag: v0.1.0) Release version 0.1.0
       Create
      EOO
  }
}

: multi-pkg
:
{
  # Create the remote repository.
  #
  +mkdir --no-cleanup prj.git
  +git -C prj.git -c init.defaultBranch=master init --bare --quiet &prj.git/***

  # Create the local repository.
  #
  +$new -t empty prj &prj/***
  +$new -t exe --package prj -d prj
  +$new -t lib --package libprj -d prj

  +$gp config user.name 'Test Script'
  +$gp config user.email 'testscript@example.com'
  +$gp remote add origin ../prj.git
  +$gp add '*'
  +$gp commit -m 'Create'
  +$gp push --set-upstream origin master

  test.options += -q

  : patch
  :
  {
    $clone_repos;

    $* --push --yes;

    $clone2;
    $log2 >>:~%EOO%;
      % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
       (tag: v0.1.0) Release version 0.1.0
       Create
      EOO

    cat prj2/prj/manifest >>~%EOO%;
      %.*
      name: prj
      version: 0.2.0-a.0.z
      language: c++
      %.*
      EOO

    cat prj2/libprj/manifest >>~%EOO%
      %.*
      name: libprj
      version: 0.2.0-a.0.z
      language: c++
      %.*
      EOO
  }

  : multiple-revisions
  :
  {
    test.arguments += --push --yes

    $clone_repos;
    $* --no-open;

    echo '' >+ prj/prj/manifest;
    $gp add prj/manifest;

    $release --revision --current-tag=update -q --yes --no-edit -d prj/prj;

    echo '' >+ prj/prj/manifest;
    $gp add prj/manifest;

    echo '' >+ prj/libprj/manifest;
    $gp add libprj/manifest;

    $* --revision --no-edit 2>>EOE != 0;
      error: different current package versions
        info: package prj version 0.1.0+1
        info: package libprj version 0.1.0
      EOE

    $* --revision --current-tag=update --no-edit 2>>~%EOE%;
      %Updated tag 'v0.1.0' \.+%d
      EOE

    # Note that the 0.1.0+1 release is not tagged as it wasn't pushed.
    #
    $clone2;
    $log2 >>:~%EOO%
      % \(HEAD -> master, tag: v0.1.0, \.*\) Release versions prj/0.1.0\+2, libprj/0.1.0\+1%d
       Release version 0.1.0+1
       Release version 0.1.0
       Create
      EOO
  }

  : prompt
  :
  {
    +$clone_repos
    sp = ' '

    : default
    :
    {
      $clone_repos;

      $* <'y' 2>>:"EOE";
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    no
        continue? [y/n]$sp
        EOE

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Create%d
        EOO
    }

    : push
    :
    {
      $clone_repos;

      $* --push  <'y' 2>>:"EOE";
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    origin/master
        continue? [y/n]$sp
        EOE

      $clone2;
      $log2 >>:~%EOO%
        % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : show-push
    :
    {
      test.arguments += --show-push

      $clone_repos;

      $* <'y' 2>>:"EOE" >>~%EOO%;
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     v0.1.0
          push:    origin/master \(show\)
        continue? [y/n]$sp
        EOE
        %git -C "?\.+prj"? push origin master refs/tags/v0.1.0%d
        EOO

      $gp push origin master refs/tags/v0.1.0;

      $clone2;
      $log2 >>:~%EOO%;
        % \(HEAD -> master, \.*\) Change version to 0.2.0-a.0.z%d
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --no-tag --no-open --yes >>~%EOO%;
        %git -C "?.+prj"? push origin master%
        EOO

      $gp push origin master;

      $pull2 --tags --force; # Updates the existing local tag (see above).
      $log2 >>:~%EOO%;
        % \(HEAD -> master, \.*\) Release version 0.2.0%d
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --tag --yes >>~%EOO%;
        %git -C "?\.+prj"? push origin refs/tags/v0.2.0%d
        EOO

      $gp push origin refs/tags/v0.2.0;

      $pull2 --tags --force; # Updates the existing local tag (see above).
      $log2 >>:~%EOO%;
        % \(HEAD -> master, tag: v0.2.0, \.*\) Release version 0.2.0%d
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO

      $* --revision --current-tag=remove --force=unchanged --yes >>~%EOO% 2>>~%EOE%;
        %git -C "?\.+prj"? push origin master refs/tags/v0.2.0\+1 :refs/tags/v0.2.0%d
        EOO
        %Deleted tag 'v0.2.0' \.+%d
        EOE

      $gp push origin master refs/tags/v0.2.0+1 ':refs/tags/v0.2.0';

      $fetch2 --prune origin '+refs/tags/*:refs/tags/*';
      $pull2 --tags;
      $log2 >>:~%EOO%
        % \(HEAD -> master, tag: v0.2.0\+1, \.*\) Release version 0.2.0\+1%d
         Release version 0.2.0
         Change version to 0.2.0-a.0.z
         (tag: v0.1.0) Release version 0.1.0
         Create
        EOO
    }

    : no-tag
    :
    {
      $clone_repos;

      $* <'y' --no-tag 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0
          open:    0.2.0-a.0.z

          commit:  yes
          tag:     no
          push:    no
        continue? [y/n]$sp
        EOE
    }

    : no-open
    :
    {
      $clone_repos;

      $* <'y' --no-open 2>>:"EOE"
        releasing:
          package: prj
          current: 0.1.0-a.0.z
          release: 0.1.0

          package: libprj
          current: 0.1.0-a.0.z
          release: 0.1.0

          commit:  yes
          tag:     v0.1.0
          push:    no
        continue? [y/n]$sp
        EOE
    }
  }
}

: options-incompatibility
:
{
  $clone_prj;

  $* --revision --open                      2>'error: both --revision and --open specified'       != 0;
  $* --revision --alpha                     2>'error: both --revision and --alpha specified'      != 0;
  $* --revision --no-open                   2>'error: both --revision and --no-open specified'    != 0;
  $* --revision --open-beta                 2>'error: both --revision and --open-beta specified'  != 0;
  $* --open     --no-tag                    2>'error: both --open and --no-tag specified'         != 0;
  $* --tag      --no-commit                 2>'error: both --tag and --no-commit specified'       != 0;
  $* --push     --no-commit                 2>'error: both --push and --no-commit specified'      != 0;
  $* --push     --show-push                 2>'error: both --push and --show-push specified'      != 0;
  $* --edit     --no-commit                 2>'error: both --no-commit and --edit specified'      != 0;
  $* --current-tag=keep                     2>'error: --current-tag requires --revision or --tag' != 0;
  $* --revision --current-tag keep --no-tag 2>'error: both --current-tag and --no-tag specified'  != 0;
  $* --open-base 1.2.3 --open-beta          2>'error: both --open-beta and --open-base specified' != 0;
  $* --amend                                2>'error: --amend requires --revision'                != 0;
  $* --squash 1                             2>'error: --squash requires --amend'                  != 0;
  $* --revision --amend --squash 0          2>'error: invalid --squash value: 0'                  != 0;
  $* --revision --amend --no-commit         2>'error: both --amend and --no-commit specified'     != 0
}
