#!/usr/bin/bash
#
# Usage: pull
#
# Pulls remote changes using rebase & tries to rebundle,
# safely stashing and re-applying your local changes, if any
#

# Colors
color_error="$(tput sgr 0 1)$(tput setaf 1)"
color_reset="$(tput sgr0)"

# Current working dir and repo base dir
current_dir="$(pwd)"
base_dir=$(git rev-parse --show-cdup)

# Pop any stashed changes
unstash() {
  if [[ ! "$stash" =~ "No local changes to save" ]]; then
    echo
    echo "🍯  Popping stash..."
    git stash pop
  fi
}

# Pop any stashed changes and exit
rollback() {
  echo
  echo "${color_error}Something went wrong, rolling back${color_reset}"
  unstash
  exit $1
}

# Test whether a command exists
# $1 - cmd to test
cmd_exists() {
  if which $1 >/dev/null 2>&1; then
    return 0
  fi
  return 1
}

# Test whether a file exists
# $1 - file to test
file_exists() {
	if [[ -r "./$base_dir$1" ]]; then
	  return 0
	fi
	return 1
}

# Go to directory of changed file
# $1 - filename
change_dir() {
	file=$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD | grep "$1")
	if [[ -a "./$base_dir$file" ]]; then
	  cd $(dirname "./$base_dir$file")
	  return 0
	fi
	return 1
}

# Go back to
reset_dir() {
    cd "$current_dir"
}

# Test wether a file has changed
# $1 - filename
has_changed() {
  # store changed files for install check (
  # ORIG_HEAD is last value of HEAD before pull
  changed_files="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD 2>/dev/null)"
  if $(echo "$changed_files" | grep --quiet "$1"); then
    return 0
  fi
  return 1
}

branch=$(git branch --no-color 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/') || exit $?
default_remote="origin"
remote=$(git config "branch.${branch}.remote" || echo "$default_remote")
remote_branch=$( (git config "branch.${branch}.merge" || echo "refs/heads/$branch") | cut -d/ -f3- )

# Stash any local changes, including untracked files
stash=$(git stash --include-untracked)

# Update our remote
echo "🚀  Fetching from $remote..."
git fetch $remote || rollback $?

# Pull, using rebase if configured
rebase="--rebase" # TODO disable if env-var is set
git pull $rebase $remote $remote_branch || rollback $?

# Update submodules
git submodule update || rollback $?

unstash

# Remove old, stale branches
git remote prune $remote >/dev/null 2>&1 &

# Bundle em if you got em!
if [ "$GIT_FRIENDLY_NO_BUNDLE" != "true" ]; then
  if cmd_exists 'bundle' && has_changed 'Gemfile'; then
    echo
    echo '⚔  Bundling gems...'
    change_dir 'Gemfile'
    bundle check >/dev/null 2>&1 || bundle install
    reset_dir
  fi
fi

# Install Node.js packages with Yarn
yarned=0
if [ "$GIT_FRIENDLY_NO_YARN" != "true" ]; then
  if file_exists 'yarn.lock' && ( has_changed 'yarn.lock' || has_changed 'package.json' ); then
    change_dir 'yarn.lock' || change_dir 'package.json'
    if cmd_exists 'yarn' || [ -x ./node_modules/.bin/yarn ]; then
      echo
      echo '⚔  Installing npm packages with yarn...'
      cmd_exists 'yarn' && yarn install || ./node_modules/.bin/yarn install
      yarned=1
    fi
    reset_dir
  fi
fi

# Install Node.js packages with npm
if [ "$GIT_FRIENDLY_NO_NPM" != "true" ]; then
  if cmd_exists 'npm' && has_changed 'package.json' && [ $yarned -eq 0 ]; then
    echo
    echo '⚔  Installing npm packages...'
    change_dir 'package.json'
    npm install
    reset_dir
  fi
fi

# Install Composer packages
if [ "$GIT_FRIENDLY_NO_COMPOSER" != "true" ]; then
  if has_changed 'composer.lock'; then
    change_dir 'composer.lock'
    if cmd_exists 'composer' || (cmd_exists 'php' && [ -f ./composer.phar ]); then
      echo
      echo '⚔  Installing Composer packages...'
      [ -f ./composer.phar ] && php composer.phar install || composer install
    fi
    reset_dir
  fi
fi

echo "🦄  Done"
exit 0
