#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
# SPDX-License-Identifier: MIT

set -e -o pipefail

# shellcheck disable=SC1091
# Imported from commons.sh when releasing:
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
# SPDX-License-Identifier: MIT

set -e -o pipefail

if [ -n "${GITTED_TESTING}" ]; then
    set -x
fi

if [ -n "${GITTED_VERBOSE}" ]; then
    set -x
fi

function title_it {
    printf '\n👉 \e[1m%s\e[0m...\n' "$@"
}

function warn_it {
    printf '\n⚠️ \e[38;5;160m%s\e[0m\n' "$@"
}

function fail_it {
    warn_it "$@"
    exit 1
}

function bash_it {
    printf '%q ' "$@" | /bin/bash -x
}

function plural {
    printf "%s %s" "$1" "$2"
    if [ "$1" != '1' ]; then
        printf 's'
    fi
}

function retry_it {
    local message="$1"
    local cmd="$2"
    local max="${3:-10}"
    local attempt=1
    while (( attempt <= max )); do
        title_it "${message} (attempt no.${attempt}/${max})"
        eval "$cmd" && return 0
        if [ -n "${GITTED_TESTING}" ]; then
            exit 1
        fi
        attempt=$(( attempt + 1 ))
        sleep 1
    done
    fail_it "Command failed after ${max} attempts: ${cmd}"
}

base=$(dirname "$0")
export base

branches="$(git branch --format='%(refname:short)')"
if [ -z "${branches}" ]; then
    master="$(git symbolic-ref --short HEAD 2>/dev/null || exit 1)"
elif echo "$branches" | grep -qx 'master'; then
    master=master
elif echo "$branches" | grep -qx 'main'; then
    master=main
else
    fail_it "Neither 'master' nor 'main' branch found."
fi
export master

if [ -z "${GIT_BIN}" ]; then
    GIT_BIN=git
    export GIT_BIN
fi
# Imported from sanity.sh when releasing:
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
# SPDX-License-Identifier: MIT

set -e -o pipefail

GITTED_GIT_MIN_MAJOR=2
GITTED_GIT_MIN_MINOR=13

function gitted_sanity_fail {
    printf '\n⚠️ \e[38;5;160m%s\e[0m\n' "$@" >&2
    exit 1
}

GIT_BIN="${GIT_BIN:-git}"

if ! command -v "${GIT_BIN}" > /dev/null 2>&1; then
    gitted_sanity_fail "Git is not installed: '${GIT_BIN}' command was not found in PATH"
fi

gitted_git_version_raw=$("${GIT_BIN}" --version 2>/dev/null | awk '{print $3}')
if [ -z "${gitted_git_version_raw}" ]; then
    gitted_sanity_fail "Failed to detect Git version (could not parse output of '${GIT_BIN} --version')"
fi
IFS='.' read -r gitted_git_major gitted_git_minor _ <<< "${gitted_git_version_raw}"
if ! [[ "${gitted_git_major}" =~ ^[0-9]+$ ]] || ! [[ "${gitted_git_minor}" =~ ^[0-9]+$ ]]; then
    gitted_sanity_fail "Failed to parse Git version '${gitted_git_version_raw}'"
fi
if (( gitted_git_major < GITTED_GIT_MIN_MAJOR )) || \
    (( gitted_git_major == GITTED_GIT_MIN_MAJOR && gitted_git_minor < GITTED_GIT_MIN_MINOR )); then
    gitted_sanity_fail "Git version ${gitted_git_version_raw} is too old; gitted requires Git ${GITTED_GIT_MIN_MAJOR}.${GITTED_GIT_MIN_MINOR}.0 or higher"
fi

if ! "${GIT_BIN}" rev-parse --git-dir > /dev/null 2>&1; then
    gitted_sanity_fail "Oops, this is not a Git repository"
fi

root=$("${GIT_BIN}" rev-parse --show-toplevel)
cd "${root}"
# Imported from intro.sh when releasing:
help_pull=$(cat << EOT
Usage: pull

We pull from the upstream branch and then also from the upstream repository.
EOT
)

help_push=$(cat << EOT
Usage: push [<message> | <branch>]

You either provide a commit message or a name of the branch you push to. If the
argument is a single integer, we treat it as a branch name. Everything else
is treated as a plain message.
EOT
)

help_branch=$(cat << EOT
Usage: branch <name>

You either provide a "message" or a name of the branch you push to. If the
argument is a single integer, we treat it as a branch name. Everything else
is treated as a plain message.
EOT
)

help_commit=$(cat << EOT
Usage: commit [<message> | <branch>]

You either provide a commit message or a name of the branch you push to. If the
argument is a single integer, we treat it as a branch name. Everything else
is treated as a plain message.
EOT
)

#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) 2025 Yegor Bugayenko
# SPDX-License-Identifier: MIT

set -e -o pipefail

if [ "$1" = '--help' ]; then
    cmd=$(basename "$0")
    var="help_${cmd}"
    help=${!var}
    if [ -z "${help}" ]; then
        echo "Usage: ${cmd} --help"
    else
        echo "${help}"
    fi
    exit
fi

if [ -z "${GITTED_INTRODUCED}" ]; then
    printf "\e[1mGitted\e[0m 0.0.21 (https://github.com/yegor256/gitted)\n"
    GITTED_INTRODUCED=true
    export GITTED_INTRODUCED
fi
if ! "${GIT_BIN}" remote | grep -q ^origin; then
    warn_it "No remote by the name 'origin', nothing to pull from"
    exit
fi

branch=$("${GIT_BIN}" symbolic-ref HEAD --short)
if [ -z "${branch}" ]; then
    fail_it "Something is wrong, the current branch was not detected"
fi

if ! "${GIT_BIN}" rev-parse --abbrev-ref --symbolic-full-name '@{u}' >/dev/null 2>&1; then
    if git rev-parse --verify HEAD > /dev/null 2>&1; then
        bash_it "${GIT_BIN}" fetch
        if "${GIT_BIN}" ls-remote --exit-code origin "refs/heads/${branch}" >/dev/null 2>&1; then
            title_it "The upstream is not configured for the ${branch} branch, setting it to origin/${branch}"
            bash_it "${GIT_BIN}" branch "--set-upstream-to=origin/${branch}" "${branch}"
        else
            title_it "The ${branch} branch does not exist at origin yet, skipping upstream setup"
        fi
    fi
fi

if [ -e .git/modules ]; then
    subs=$(grep -c '^\[submodule' .gitmodules)
    title_it "Updating $(plural "${subs}" 'submodule') first..."
    bash_it "${GIT_BIN}" submodule deinit -f .
    retry_it "Initializing submodules" "bash_it ${GIT_BIN} submodule update --init"
    while read -r key path; do
        name=${key#submodule.}
        name=${name%.path}
        bash_it "${GIT_BIN}" config "submodule.${name}.ignore" all
        (cd "${path}" && bash_it "${GIT_BIN}" reset --hard)
    done < <("${GIT_BIN}" config --file .gitmodules --get-regexp '^submodule\..*\.path$')
    retry_it "Updating submodules" "bash_it ${GIT_BIN} submodule update --remote"
fi

inc=$(${GIT_BIN} status --porcelain | grep -c '^??' || true)
if [ ! "${inc}" == '0' ]; then
    title_it "Staging $(plural "${inc}" 'file')"
    bash_it "${GIT_BIN}" add .
fi

stashed=no
if git rev-parse --verify HEAD > /dev/null 2>&1; then
    if ! git diff --quiet || ! git diff --cached --quiet; then
        trap "title_it 'Applying the changes back' && bash_it \${GIT_BIN} stash apply" EXIT
        df=$(${GIT_BIN} status --porcelain | wc -l | xargs)
        title_it "Stashing $(plural "${df}" 'file')"
        bash_it "${GIT_BIN}" stash
        stashed=yes
    fi
fi

# shellcheck disable=SC2154
if git ls-remote --exit-code origin "refs/heads/${master}" > /dev/null 2>&1; then
    retry_it "Pulling from origin/${branch}" "bash_it ${GIT_BIN} pull origin ${branch}"
fi

if ${GIT_BIN} remote | cut -f1 -d ' ' | grep upstream; then
    if [ "${branch}" != "${master}" ]; then
        title_it "Checking out the origin/${master} branch"
        bash_it "${GIT_BIN}" checkout "${master}"
    fi
    title_it "Pulling from upstream"
    bash_it "${GIT_BIN}" pull upstream "${master}"
    if [ "${branch}" != "${master}" ]; then
        title_it "Checking out the origin/${branch} branch"
        bash_it "${GIT_BIN}" checkout "${branch}"
        title_it "Merging ${master} into ${branch}"
        bash_it "${GIT_BIN}" merge "${master}"
    fi
fi

trap - EXIT
if [ "${stashed}" == 'yes' ]; then
    title_it "Applying the changes back"
    bash_it "${GIT_BIN}" stash apply
fi
