#!/bin/bash
# Version: 0.1.0
# Copyright (c) 2023:
#   Darren 'Tadgy' Austin <darren (at) afterdark.org.uk>
# Licensed under the terms of the GNU General Public License version 3.
#
# Automatically fetch and merge (if possible) upstream changes into a local git repository.

# What platform we're running on.
PLATFORM="$(uname -s)"

# Only operate on repositories that are set for maintenance work in ~/.gitconfig.
while IFS= read -r REPO; do
  # Get into the working directory.
  pushd "$REPO" >/dev/null 2>&1 || {
    printf "%s: %s\\n" "${0##*/}" "failed to change to working directory '$REPO'" >&2
    RET=1
    continue
  }

  # Skip processing if auto-merging is disabled in this repository.
  [[ "$(git config --local --get --type bool script.AutoMergeDisabled 2>/dev/null)" != "true" ]] && {
    # Fetch the latest updates.
    if git fetch --all --atomic --tags >/dev/null 2>&1; then
      # Try to merge using fast-forward only, so no clobbering is done.
      if MERGE="$(git merge --ff-only 2>/dev/null)"; then
        [[ "$(printf "%s" "$MERGE" | awk '{ print $1; exit }')" == "Updating" ]] && {
          if [[ "$PLATFORM" == "Linux" ]]; then
            # Linux, how I love thee.
            NOW="$(date +'%s%3N')"
          elif [[ "$PLATFORM" == "Darwin" ]]; then
            # This sucks... but so does Darwin.
            NOW="$(perl -e 'use Time::HiRes; printf "%.3f", Time::HiRes::time();')"
            NOW="${NOW/.}"
          else
            printf "%s: %s\\n" "${0##*/}" "unsupported platform: $PLATFORM" >&2
            RET=2
            break
          fi
          git config --local --replace-all --type int script.AutoMergeLast "$NOW" >/dev/null 2>&1 || {
            printf "%s: %s\\n" "${0##*/}" "updating AutoMergeLast timestamp failed for '$REPO'" >&2
            RET=1
          }
          git config --local --replace-all --type bool script.AutoMergeSuccess true >/dev/null 2>&1 || {
            printf "%s: %s\\n" "${0##*/}" "updating AutoMergeSuccess marker failed for '$REPO'" >&2
            RET=1
          }
        }
      else
        git config --local --replace-all --type bool script.AutoMergeSuccess false >/dev/null 2>&1 || {
          printf "%s: %s\\n" "${0##*/}" "updating AutoMergeSuccess marker failed for '$REPO'" >&2
          RET=1
        }
      fi
    else
      # Mark that the auto-merge failed.
      printf "%s: %s\\n" "${0##*/}" "git fetch failed for '$REPO'" >&2
      git config --local --replace-all --type bool script.AutoMergeSuccess false >/dev/null 2>&1 || {
        printf "%s: %s\\n" "${0##*/}" "updating AutoMergeSuccess marker failed for '$REPO'" >&2
        RET=1
      }
    fi
  }

  # Back to where we started...
  popd >/dev/null 2>&1 || continue
done < <(git config --global --get-all maintenance.repo 2>/dev/null)

exit "${RET:-0}"
