# Tools
alias g="git"

y() {
  local unignore="$XDG_CONFIG_HOME/yadm/unignore"
  test -r "$unignore" && cat "$unignore" | while read f; do eval ls -d $f; done | yadm add --intent-to-add --pathspec-from-file=-
  test "$#" -eq 0 && yadm s || yadm "$@"
}
yc() {
  local folder="${1:-${PWD/$XDG_CONFIG_HOME\/}}"
  echo "config/$folder:" >/tmp/yc-msg
  yadm commit -v --template /tmp/yc-msg ${@:2} -- "$XDG_CONFIG_HOME/$folder*"
}

gcn() {
  local root="$(git rev-parse --show-toplevel)"
  if test "$root" = "$DATA"
  then
    if test $# -eq 0 || test -e "$1"
    then
      fulldir="$(realpath ${1:-$PWD})"
      dir="${fulldir#$root/*/}"
    else
      dir="box/$1"
      fulldir="$root/2-standards/$dir"
    fi
  else
      fulldir="$(realpath ${1:-$PWD})"
      dir="${fulldir#$root/}"
  fi
  echo "$dir: " >/tmp/gcn-msg
  git add $fulldir
  git moves -q
  git commit -v --template /tmp/gcn-msg ${@:2} #-- $fulldir
}

# if in home or under XDG_CONFIG_HOME and not within a git directory, replace git by yadm
git() {
  case "$1" in
    (reset) test "$2" = "--hard" && return 66;;
    (config) ;;
    (clone) ;;
    (*) case "$PWD" in
      ($HOME|$XDG_CONFIG_HOME|$LAST_YADM)
        yadm "$@"
        return $?
        ;;
      ($XDG_CONFIG_HOME*|$HOME/.local*)
        if ! command git rev-parse --show-toplevel &>/dev/null; then
          export LAST_YADM="$PWD"
          yadm "$@"
          return $?
        fi;;
    esac;;
  esac
  command git "$@"
}

# BRANCHES
# Remove list of tags local & remote
gitrmtag() {
  declare -a refs
  local index=1
  for tag in $@; do refs[index++]=":refs/tags/$tag"; done
  git push origin "${refs[@]}" && git tag -d "$@"
}
# Rename a tag
gitretag() {
  git push origin refs/tags/${1}:refs/tags/${2} :refs/tags/$1 && git tag -d $1
}

# Rewriting history {{{

# gets the AuthorDate of a given committish
git-authordate() {
  local date=$(git log --pretty=fuller --date=raw -1 $1 | grep AuthorDate)
  echo ${date##*: }
}
# executes a git command (usually commit) with the date of a given committish
git-withdate() {
  local date=$(git-authordate $1)
  GIT_AUTHOR_DATE="$date" GIT_COMMITTER_DATE="$date" git "${@:2}"
}

# takes all changes in the current working tree and amends them to the given commit
gitedit() {
  git stash
  gitcommits -q $1
  git reset --keep $1
  git stash pop
  git-withdate $1 commit --all --amend "${@:2}"
  gitcommits
}

# takes two committishs and squashes them with all commits between them into a single commit
# this will rewrite the full history from then on, but should not create any conflicts
gitsquash() {
  local -a options
  while [ $# -gt 0 ]; do
    case $1 in
      -i) local ignore=true; shift 1;;
      -f|--force) local force=true; shift 1;;
      -*) options+=($1); exit 1;;
      *) break;;
    esac
  done

  ((#!=2)) && echo "Usage: [options] <startcommit> <endcommit>" && return 1
  [[ -n $(git status -s) ]] && [ ! $force ] && echo -e "Tree is dirty, commit or stash your changes first!\nIf you want to execute the command regardless, run again with --force" && return 1

  local 1=$(git rev-parse $1)
  local 2=$(git rev-parse $2)
  [ $(git rev-list $1 --count) -lt $(git rev-list $2 --count) ] && t=$1 && 1=$2 && 2=$t

  gitcommits -q $1
  git reset --hard $1
  if [ $(git rev-list $2 --count) = 1 ]; then
    git update-ref -d HEAD
    git add .
    git-withdate $1 commit -c $1
  else
    git reset -q $2
    git add .
    git commit --amend
  fi
  gitcommits
}

# given a committish, this command saves a list of commits between the HEAD and the given committish into the .git directory
# when ran without parameters it applies the saved list of commits onto the current HEAD
gitcommits() {
  local verbosity=1
  while [[ $# -gt 0 ]]; do
    case $1 in
      -v) verbosity=2; shift 1;;
      -q|--quiet) verbosity=0; shift 1;;
      --theirs) local params=(-X theirs); shift 1;;
      *) break;;
    esac
  done

  local stashed="$(git rev-parse --show-toplevel)/.git/stashed-commits"
  if [ $1 ]; then
    if [ $verbosity -eq 0 ]
      then git rev-list --reverse HEAD...$1 >$stashed
      else git rev-list --reverse HEAD...$1 | tee $stashed
    fi
  else
    local aborted
    for commit in $(cat $stashed); do
      [ $aborted ] && rest+=($commit) && continue
      [ $verbosity -gt 0 ] && git --no-pager log --oneline -1 $commit
      git-withdate $commit cherry-pick $commit ${params:0} >/dev/null
      local last=$?
      [ $last -gt 0 ] && local aborted=true && typeset -a rest && continue
      [ $verbosity -gt 0 ] && echo -e "\e[1A$(git log --color=always --pretty=format:"%C(yellow)$(git rev-parse --short HEAD^^)%C(bold) -> %Creset%C(yellow)%h%Creset %s" -1)"
      [ $verbosity -gt 1 ] && git status -s
    done
    echo $rest >$stashed
    [ $aborted ] && echo "A problem was encountered. Fix it and run 'gitcommits' again to apply the remaining ${#rest} commits."
  fi
}
# }}}

# Testing
gittestcommit() { touch file$((++i)) && git add 'file*' && git commit -m "Create file$i"; }
