# $Id$
# vim:et:ft=sh:sts=2:sw=2
#
# git-flow -- A collection of Git extensions to provide high-level
# repository operations for Vincent Driessen's branching model.
#
# A blog post presenting this model is found at:
#    http://blog.avirtualhome.com/development-workflow-using-git/
#
# Feel free to contribute to this project at:
#    http://github.com/petervanderdoes/gitflow
#
# Authors:
# Copyright 2012-2017 Peter van der Does. All rights reserved.
#
# Original Author:
# Copyright 2010 Vincent Driessen. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

usage() {
	OPTIONS_SPEC="\
git flow init

Initialize a new git repo with support for the branching model.

For more specific help type the command followed by --help
--
"
	flags_help
}

# Parse arguments and set common variables
parse_args() {
	FLAGS "$@" || exit $?
	eval set -- "${FLAGS_ARGV}"
}

# Default entry when no SUBACTION is given
cmd_default() {
	OPTIONS_SPEC="\
git flow init [-h] [-d] [-f]

Setup a git repository for git flow usage. Can also be used to start a git repository.
--
h,help!             Show this help
showcommands!       Show git commands while executing them
d,[no]defaults      Use default branch naming conventions
f,[no]force         Force setting of gitflow branches, even if already configured

Use config file location
local!   use repository config file
global!  use global config file
system!  use system config file
file=    use given config file
"
	local gitflow_config_option should_check_existence branchcount guess
	local master_branch develop_branch default_suggestion answer prefix

	# Define flags
	DEFINE_boolean 'force' false 'force setting of gitflow branches, even if already configured' f
	DEFINE_boolean 'defaults' false 'use default branch naming conventions' d
	DEFINE_boolean 'local' false 'use repository config file'
	DEFINE_boolean 'global' false 'use global config file'
	DEFINE_boolean 'system' false 'use system config file'
	DEFINE_string 'file' "" 'use given config file'

	# Override defaults with values from config
	gitflow_override_flag_boolean   "init.defaults"   "defaults"

	# Parse arguments
	parse_args "$@"

	if [ "$FLAGS_file" != "" ]; then
		gitflow_config_option="--file $FLAGS_file"
	elif flag local; then
		gitflow_config_option="--local"
	elif flag global; then
		gitflow_config_option="--global"
	elif flag system; then
		gitflow_config_option="--system"
	else
		gitflow_config_option=""
	fi

	if git_config_bool_exists "user.useconfigonly"; then
		user_email=$(git config --get user.email)
		user_name=$(git config --get user.name)
		if [ -z "${user_email}" ] || [ -z "${user_name}" ]; then
			die "Configuration useconfigonly is set but no name and/or email was set"
		fi
	fi

	if ! git rev-parse --git-dir >/dev/null 2>&1; then
		git_do init
	else
		# Assure that we are not working in a repo with local changes
		git_repo_is_headless || require_clean_working_tree
	fi

	# Running git flow init on an already initialized repo is fine
	if gitflow_is_initialized && ! flag force; then
		warn "Already initialized for gitflow."
		warn "To force reinitialization, use: git flow init -f"
		exit 0
	fi

	if flag defaults; then
		warn "Using default branch names."
	fi

	# Add a master branch if no such branch exists yet

	if gitflow_has_master_configured && ! flag force; then
		master_branch=$(git config --get gitflow.branch.master)
	else
		# Two cases are distinguished:
		# 1. A fresh git repo (without any branches)
		#    We will create a new master/develop branch for the user
		# 2. Some branches do already exist
		#    We will disallow creation of new master/develop branches and
		#    rather allow to use existing branches for git-flow.
		branch_count=$(git_local_branches | wc -l)
		if [ "$branch_count" -eq 0 ]; then
			echo "No branches exist yet. Base branches must be created now."
			should_check_existence=NO
			default_suggestion=$(git config --get gitflow.branch.master || echo master)
		else
			echo
			echo "Which branch should be used for bringing forth production releases?"
			git_local_branches | sed 's/^.*$/   - &/g'

			should_check_existence=YES
			default_suggestion=
			for guess in $(git config --get gitflow.branch.master) 'production' 'main' 'master'; do
				if git_local_branch_exists "$guess"; then
					default_suggestion="$guess"
					break
				fi
			done

		fi

		if [ -z $default_suggestion ] && flag defaults; then
				should_check_existence=YES
				default_suggestion=$(git config --get gitflow.branch.master || echo master)
		fi

		printf "Branch name for production releases: [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		master_branch=${answer:-$default_suggestion}

		# Check existence in case of an already existing repo
		if [ "$should_check_existence" = "YES" ]; then
			# If no local branch exists and a remote branch of the same
			# name exists, checkout that branch and use it for master
			if ! git_local_branch_exists "$master_branch" && git_remote_branch_exists "origin/$master_branch"; then
				git_do branch "$master_branch" "origin/$master_branch" >/dev/null 2>&1
			elif ! git_local_branch_exists "$master_branch"; then
				die "Local branch '$master_branch' does not exist."
			fi
		fi

		# Store the name of the master branch
		git_do config $gitflow_config_option gitflow.branch.master "$master_branch"
	fi

	# Add a develop branch if no such branch exists yet
	if gitflow_has_develop_configured && ! flag force; then
		develop_branch=$(git config --get gitflow.branch.develop)
	else
		# Again, the same two cases as with the master selection are
		# considered (fresh repo or repo that contains branches)
		branch_count=$(git_local_branches | grep -v "^${master_branch}\$" | wc -l)
		if [ "$branch_count" -eq 0 ]; then
			should_check_existence=NO
			default_suggestion=$(git config --get gitflow.branch.develop || echo develop)
		else
			echo
			echo "Which branch should be used for integration of the \"next release\"?"
			git_local_branches | grep -v "^${master_branch}\$" | sed 's/^.*$/   - &/g'

			should_check_existence=YES
			default_suggestion=
			for guess in $(git config --get gitflow.branch.develop) 'develop' 'int' 'integration' 'master'; do
				if git_local_branch_exists "$guess" && [ "$guess" != "$master_branch" ]; then
					default_suggestion="$guess"
					break
				fi
			done
		fi

		if [ -z $default_suggestion ] && flag defaults; then
			should_check_existence=YES
			default_suggestion=$(git config --get gitflow.branch.develop || echo develop)
		fi

		printf "Branch name for \"next release\" development: [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		develop_branch=${answer:-$default_suggestion}

		if [ "$master_branch" = "$develop_branch" ]; then
			die "Production and integration branches should differ."
		fi

		# Check existence in case of an already existing repo
		if [ "$should_check_existence" = "YES" ]; then
			git_local_branch_exists "$develop_branch" || die "Local branch '$develop_branch' does not exist."
		fi

		# Store the name of the develop branch
		git_do config $gitflow_config_option gitflow.branch.develop "$develop_branch"
	fi

	# Creation of HEAD
	# ----------------
	# We create a HEAD now, if it does not exist yet (in a fresh repo). We need
	# it to be able to create new branches.
	local created_gitflow_branch=0
	if ! git rev-parse --quiet --verify HEAD >/dev/null 2>&1; then
		git_do symbolic-ref HEAD "refs/heads/$master_branch"
		git_do commit --allow-empty --quiet -m "Initial commit"
		created_gitflow_branch=1
	fi

	# Creation of master
	# ------------------
	# At this point, there always is a master branch: either it existed already
	# (and was picked interactively as the production branch) or it has just
	# been created in a fresh repo

	# Creation of develop
	# -------------------
	# The develop branch possibly does not exist yet.  This is the case when,
	# in a git init'ed repo with one or more commits, master was picked as the
	# default production branch and develop was "created".  We should create
	# the develop branch now in that case (we base it on master, of course)
	if ! git_local_branch_exists "$develop_branch"; then
		if git_remote_branch_exists "origin/$develop_branch"; then
			git_do branch "$develop_branch" "origin/$develop_branch" >/dev/null 2>&1
		else
			git_do branch --no-track "$develop_branch" "$master_branch"
		fi
		created_gitflow_branch=1
	fi

	# Assert the git-flow repo has been correctly initialized
	gitflow_is_initialized

	# Switch to develop branch if its newly created
	if [ $created_gitflow_branch -eq 1 ]; then
		git_do checkout -q "$develop_branch" || die "Could not check out branch '$develop_branch'."
	fi

	# Ask the user for naming conventions (branch and tag prefixes )
	if flag force || \
		! git config --get gitflow.prefix.feature >/dev/null 2>&1 ||
		! git config --get gitflow.prefix.bugfix >/dev/null 2>&1 ||
		! git config --get gitflow.prefix.release >/dev/null 2>&1 ||
		! git config --get gitflow.prefix.hotfix >/dev/null 2>&1 ||
		! git config --get gitflow.prefix.support >/dev/null 2>&1 ||
		! git config --get gitflow.prefix.versiontag >/dev/null 2>&1; then
		echo
		echo "How to name your supporting branch prefixes?"
	fi

	# Feature branches
	if ! git config --get gitflow.prefix.feature >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.feature || echo feature/)
		printf "Feature branches? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.feature "$prefix"
	fi

	# Bugfix branches
	if ! git config --get gitflow.prefix.bugfix >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.bugfix || echo bugfix/)
		printf "Bugfix branches? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.bugfix "$prefix"
	fi

	# Release branches
	if ! git config --get gitflow.prefix.release >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.release || echo release/)
		printf "Release branches? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.release "$prefix"
	fi

	# Hotfix branches
	if ! git config --get gitflow.prefix.hotfix >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.hotfix || echo hotfix/)
		printf "Hotfix branches? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.hotfix "$prefix"
	fi

	# Support branches
	if ! git config --get gitflow.prefix.support >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.support || echo support/)
		printf "Support branches? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.support "$prefix"
	fi

	# Version tag prefix
	if ! git config --get gitflow.prefix.versiontag >/dev/null 2>&1 || flag force; then
		default_suggestion=$(git config --get gitflow.prefix.versiontag || echo "")
		printf "Version tag prefix? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && prefix= || prefix=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.prefix.versiontag "$prefix"
	fi

	# Paths
	if ! git config --get gitflow.path.hooks >/dev/null 2>&1 || flag force; then
		DOT_GIT_DIR=$(git rev-parse --git-dir)
		DOT_GIT_DIR=$(cd "$DOT_GIT_DIR" >/dev/null 2>&1 && pwd)
		default_suggestion=$(git config --get gitflow.path.hooks || git config --get core.hooksPath || echo "$DOT_GIT_DIR"/hooks)
		printf "Hooks and filters directory? [$default_suggestion] "
		if noflag defaults; then
			read answer
		else
			printf "\n"
		fi
		[ "$answer" = "-" ] && hooks_dir= || hooks_dir=${answer:-$default_suggestion}
		git_do config $gitflow_config_option gitflow.path.hooks "$hooks_dir"
	fi

	# TODO: what to do with origin?
}

cmd_help() {
	usage
	exit 0
}
