#!/bin/bash
#
# $Id: maintain_current_git.sh,v 1.18 2026/02/13 12:49:32 eha Exp eha $
#
# Maintain slackware64-current updates in git.
#
# Examples of use:
# + A crontab entry which checks for updates every night at 06:00, and
#   updates the local git repository (if updates were found):
#   00 6 * * *   /usr/local/bin/maintain_current_git.sh
# + Do not let this script run in parallel with the mirror-slackware-current
#   script. In cron, schedule this script immediately after the mirror script
#   so that you know that the local mirror is up to date.
#
# ===========================================================================
#
# - Check the local mirror to see if the ChangeLog.txt is newer than our
#   most recent git commit.
# - If older, then no further actions are needed (abort script)
# - If ChangeLog.txt has changed since our last run, do the following:
#   * rsync the updates in slackware64-current tree to a local tree
#   * generate RSS file from the ChangeLog.txt
#   * update local git repository with the modifications
#
# Author: Eric Hameleers <alien@slackware.com>
#
# ===========================================================================

# Read configuration file if it exists;
#  - commonly called 'maintain_current_git.conf'
CONFFILE=${CONFFILE:-"$(dirname $0)/$(basename $0 .sh).conf"}
[ -f ${CONFFILE} ] && . ${CONFFILE}

# The script's basename will be displayed in the RSS feed:
BASENAME=$( basename $0 )

# Try not to be verbose by default:
DEBUG=${DEBUG:-0}

# Location of the original script:
ORIGSCR="http://www.slackware.com/~alien/tools/maintain_current_git.sh"

# We prevent this script from running more than one instance:
# If you have a reason to want to run multiple instances at the same time, just
# make a symlink to this script and run the script using the symlinked name.
# This will create a unique PIDFILE name.
PIDFILE=/var/tmp/$(basename $0 .sh).pid

# The script's revision number will be displayed in the RSS feed:
REV=$( echo "$Revision: 1.18 $" | cut -d' '  -f2 )

# Where we will write temporary files (like the downloaded ChangeLog.txt):
TMP=${TMP:-/tmp}

# ---------------------------------------------------------------------------

# Distro related variables:

# Name of our distro:
DISTRO=${DISTRO:-slackware}

# Architecture can be 'x86_64' meaning 64bit or 'x86' for 32bit.
# The value of ARCH determines the name of the slackware directories.
# This value can be overruled via the '-a' commandline parameter;
ARCH=${ARCH:-"x86_64"}

# The slackware release we're mirroring (defaults to 'current').
# You can use the script's '-r' switch to alter this to another release,
# for instance mirror Slackware 15.0 by passing '-r 15.0' to the script.
RELEASE=${RELEASE:-"current"}

# Where is the local mirror? The Slackware directory tree below
# should be named ${SLACKROOTDIR}/${SLACKRELEASE}
# This value can be overruled via the '-l' commandline parameter;
SLACKROOTDIR=${SLACKROOTDIR:-"/mirrors/${DISTRO}"}

# Normally, when no update is found in the ChangeLog.txt, the script exits
# without touching the git repository. To force that regardless, you set
# the variable FORCE to "yes" (or pass the '-f' switch to the script).
FORCE=${FORCE:-"no"}

# By default, the script will start with cloning the remote repository in
# case it discovers that you do not have a local git repository clone yet.
# If you prefer to start with an empty new local git repository (or if the
# remote repository does not yet exist) then set the variable INITNEW
# to "yes" (or pass the '-i' switch to the script).
INITNEW=${INITNEW:-"no"}

# By default, the script updates the git repository when updates are found.
# Set the variable ONLYDIFF to '1 or pass '-n' to the script
# to merely get notified about changes in the ChangeLog.txt:
ONLYDIFF=${ONLYDIFF:-0}

# ---------------------------------------------------------------------------

# Git related variables:

# Default author/committer for this git repository:
AUTHOR_NAME=${AUTHOR_NAME:-"Patrick J Volkerding"}
AUTHOR_EMAIL=${AUTHOR_EMAIL:-"volkerdi@slackware.com"}
COMMITTER_NAME=${COMMITTER_NAME:-"Eric Hameleers"}
COMMITTER_EMAIL=${COMMITTER_EMAIL:-"alien@slackware.com"}

# Name of the git repository storing the updates to the source tree:
REPONAME=${REPONAME:-current}

# Default branch to update is 'master'; use this variable or "-b" to alter that:
BRANCH=${BRANCH:-"master"}

# Path to the local slackware64-current git repository:
GITDIR=${GITDIR:-"/home/git/repositories/${REPONAME}"}

# URI of the remote slackware64-current git repository:
GITURI=${GITURI:-"git://slackware.nl"}

# Browser-accessible web URL to the git repository:
GITURL=${GITURL:-"https://git.slackware.nl"}

# You can specify a custom commit message instead of using the latest
# update to the ChangeLog.txt by setting 'CUSTOM_COMMIT' to a filename
# or specifying the "-m" parameter:
CUSTOM_COMMIT=${CUSTOM_COMMIT:-""}

# ---------------------------------------------------------------------------

# Variables for RSS feed generation:

# The title of the generated RSS feed:
RSS_TITLE=${RSS_TITLE:-"${DISTRO^}-${RELEASE} (${ARCH}) ChangeLog.txt"}

# The logo picture used for the RSS feed:
RSS_ICON=${RSS_ICON:-"http://www.slackware.com/~alien/graphics/blueorb.png"}

# The URL linked to when clicking on the logo:
RSS_LINK=${RSS_LINK:-"http://www.slackware.com/"}

# URL to the full changelog.txt:
RSS_CLURL=${RSS_CLURL:-"${GITURL}/${REPONAME}/tree/ChangeLog.txt"}

# The descriptive text for the RSS feed:
RSS_DESCRIPTION=${RSS_DESCRIPTION:-"Tracking ${DISTRO^} development in git."}

# Maximum number of RSS feed entries to display, '0' means no limit:
RSS_FEEDMAX=${RSS_FEEDMAX:-"0"}

# The RSS generator must use a unique feed identifier.
# Generate one for your feed by using the string returned by "uuidgen -t":
RSS_UUID=${RSS_UUID:-"c964f45e-6732-11e8-bbe5-107b4450212f"}

# ---------------------------------------------------------------------------

# Function definitions:

#
# Generate RSS from the ChangeLog.txt
#
function rss_changelog {
  # Create a RSS feed out of the ChangeLog.txt
  # Argument #1 : full path to a directory
  # Argument #2 : a filename (defaults to 'ChangeLog.txt')
  # Argument #2 : a filename (defaults to 'ChangeLog.rss')

  if [ ! -d "$1" ]; then
    echo "Required argument '$1' must be an existing directory!"
    exit 1
  fi
  DIR=$1
  INFILE=${DIR}/${2:-ChangeLog.txt}
  RSSFILE=${DIR}/${3:-ChangeLog.rss}
  if [ -e  $RSSFILE -a ! -w $RSSFILE ]; then
    echo "Can not write to RSS file ${RSSFILE}!"
    exit 1
  fi

  # These values are all set in the beginning (can be user-overridden):
  TITLE="$RSS_TITLE"
  LINK="$RSS_LINK"
  ICON="$RSS_ICON"
  CLURL="$RSS_CLURL"
  DESCRIPTION="$RSS_DESCRIPTION"
  FEEDMAX=$RSS_FEEDMAX
  UUID="$RSS_UUID"

  PUBDATE=""
  LASTBUILDDATE=$(LC_ALL=C TZ=GMT date +"%a, %e %b %Y %H:%M:%S GMT")
                 # The 'date -R' RFC-2822 compliant string
                 # does not work for Thunderbird!
  counter=0

  # Parse the input file
  cat ${INFILE} | while IFS= read cline ; do
    if [ "$PUBDATE" == "" ]; then
      # PUBDATE is empty, means we're reading the first line of input.
      # The first line contains the most recent pubdate.
      # For backward compatibility, if the file starts with
      # "+--------------------------+" then we just skip that.
      [ "$cline" == "+--------------------------+" ] && read cline
      PUBDATE=$(LC_ALL=C TZ=GMT date +"%a, %e %b %Y %H:%M:%S GMT" -d "$cline")
      rm -f ${RSSFILE}
      cat <<-_EOT_ > ${RSSFILE}
	<?xml version="1.0" encoding="iso-8859-1"?>
	<rss version="2.0">
	   <channel>
	      <title>${TITLE}</title>
	      <link>${LINK}</link>
	      <image>
	        <title>${DISTRO^}</title>
	        <url>${ICON}</url>
	        <link>${LINK}</link>
	      </image>
	      <description>${DESCRIPTION}</description>
	      <language>en-us</language>
	      <id xmlns="http://www.w3.org/2005/Atom">urn:uuid:${UUID}</id>
	      <pubDate>${PUBDATE}</pubDate>
	      <lastBuildDate>${LASTBUILDDATE}</lastBuildDate>
	      <generator>${BASENAME} v ${REV}</generator>
	      <item>
	         <title>${PUBDATE}</title>
	         <pubDate>${PUBDATE}</pubDate>
	         <link>${GITURL}/${REPONAME}/tag/?h=$(LC_ALL=C TZ=UTC date -d "${PUBDATE}" +%Y%m%d%H%M%S)</link>
	         <guid isPermaLink="false">$(LC_ALL=C TZ=UTC date -d "${PUBDATE}" +%Y%m%d%H%M%S)</guid>
	         <description>
	           <![CDATA[<pre>
	_EOT_
    elif [ "$cline" == "+--------------------------+" ]; then
      # This line masrks the start of a new entry.
      # Only dump a certain amount of recent entries.
      [ $FEEDMAX -gt 0 -a $counter -gt $FEEDMAX ] && break

      # Close the previous entry:
      cat <<-_EOT_ >> ${RSSFILE}
	           </pre>]]>
	         </description>
	      </item>
	_EOT_

      # Next line is the pubdate for the next entry:
      read PUBDATE
      PUBDATE=$(LC_ALL=C TZ=GMT date +"%a, %e %b %Y %H:%M:%S GMT" -d "$PUBDATE")

      # Write the header for the next entry:
      cat <<-_EOT_ >> ${RSSFILE}
	      <item>
	         <title>${PUBDATE}</title>
	         <pubDate>${PUBDATE}</pubDate>
	         <link>${GITURL}/${REPONAME}/tag/?h=$(LC_ALL=C TZ=UTC date -d "${PUBDATE}" +%Y%m%d%H%M%S)</link>
	         <guid isPermaLink="false">$(LC_ALL=C TZ=UTC date -d "${PUBDATE}" +%Y%m%d%H%M%S)</guid>
	         <description>
	           <![CDATA[<pre>
	_EOT_

      counter=$(( ${counter}+1 ))
    else
      # Add a line of description
      [ "${cline}" != "" ] && echo "${cline}" >> ${RSSFILE}
    fi
  done

  # Close the last entry:
  cat <<-_EOT_ >> ${RSSFILE}
	           </pre>]]>
	         </description>
	      </item>
	_EOT_

  # Close the XML output:
  cat <<-_EOT_ >> ${RSSFILE}
	   </channel>
	</rss>
	_EOT_
}

#
# Output a mknod commandline for a 'tar tvf' input line of a device file:
#
function tarline2mknod() {
  # Example line which should be passed as arguments (no quotes):
  # "brw-r----- root/root      36,3 2009-06-16 00:59 dev/eda3"

  local LINE=$(echo $* | tr -s ' ')
  local TYPE=$(echo $LINE |cut -c1)
  local MAJ=$(echo $LINE |cut -d' ' -f3 |cut -d, -f1)
  local MIN=$(echo $LINE |cut -d' ' -f3 |cut -d, -f2)
  local DEVNAM="$(echo $LINE |cut -d' ' -f6)"
  local UMOD=$(echo $LINE |cut -c2-4 |tr 'rwx-' '1110')
  local GMOD=$(echo $LINE |cut -c5-7 |tr 'rwx-' '1110')
  local OMOD=$(echo $LINE |cut -c8-10 |tr 'rwx-' '1110')

  echo "mkdir -p $(dirname $DEVNAM)"
  echo "mknod -m $((2#${UMOD}))$((2#${GMOD}))$((2#${OMOD})) $DEVNAM $TYPE $MAJ $MIN"
}

#
# Generate script which re-creates tarball device-nodes (requires root):
#
function mknodescript() {
  # This script calls 'tarline2mknod' function.
  local _ARCHIVE="$1"
  local _SCRIPT="$2"

  rm -f ${_SCRIPT} 2>/dev/null
  mkdir -p $(dirname ${_SCRIPT})
  echo "# Run this script in the root of the skeleton tree" > ${_SCRIPT}
  echo "# to re-create the required device nodes" >> ${_SCRIPT}
  tar tvf ${_ARCHIVE} | grep -E '^(b|c)' \
    | while read TARDEV ; do \
      tarline2mknod $TARDEV \
        >> ${_SCRIPT}
    done
  chmod 755 ${_SCRIPT}
}

#
# Decompression routine:
#
function _decompress() {
  SOURCE=$1
  if $(file ${SOURCE} | grep -qi ": XZ"); then
    [ $DEBUG -gt 1 ] && echo "-- Decompress: xz -d ${SOURCE}"
    xz -d ${SOURCE}
  elif $(file ${SOURCE} | grep -qi ": gzip"); then
    [ $DEBUG -gt 1 ] && echo "-- Decompress: gunzip -d ${SOURCE}"
    gunzip -d ${SOURCE}
  elif $(file ${SOURCE} | grep -qi ": bzip2"); then
    [ $DEBUG -gt 1 ] && echo "-- Decompress: bunzip2 -d ${SOURCE}"
    bunzip2 -d ${SOURCE}
  elif $(file ${SOURCE} | grep -qi ": zip"); then
    [ $DEBUG -gt 1 ] && echo "-- Decompress: unzip ${SOURCE}"
    unzip ${SOURCE}
  elif $(file ${SOURCE} | grep -qi ": lzip"); then
    [ $DEBUG -gt 1 ] && echo "-- Decompress: lzip -d ${SOURCE}"
    lzip -d ${SOURCE}
  else
    [ $DEBUG -gt 1 ] && echo "** Unknown compression type for '${1}'"
  fi
}

# Required to call the functions with xargs, and use variables in a subshell:
export -f _decompress
export DISTRO DEBUG

# ---------------------------------------------------------------------------

# Start the work.

# Make sure the PID file is removed when we kill the process
trap 'rm -f $PIDFILE; exit 1' TERM INT

while getopts "a:b:cg:hil:m:nqr:v" Option
do
  case $Option in
    h ) cat <<-"EOH"
	-----------------------------------------------------------------
	$Id: maintain_current_git.sh,v 1.18 2026/02/13 12:49:32 eha Exp eha $
	-----------------------------------------------------------------
	EOH
        echo "Usage:"
        echo "  $0 [OPTION] ..."
        echo "or:"
        echo "  SLACKROOTDIR=/your/repository/dir $0 [OPTION] ..."
        echo ""
        echo "The SLACKROOTDIR is the directory that contains the directories"
        echo "  slackware-<RELEASE> and slackware-<RELEASE>-iso"
        echo "Current value of SLACKROOTDIR : $SLACKROOTDIR"
        echo ""
        echo "You can change the script defaults in a file '$(basename $0 .sh).conf'"
        echo ""
        echo "The script's parameters are:"
        echo "  -h            This help."
        echo "  -a <arch>     Architecture to mirror (defaults to '$ARCH',"
        echo "                values can be 'x86' or 'x86_64')."
        echo "  -b <branch>   Specify branch other than '$BRANCH' to update."
        echo "                Branch (-b) and release (-r) need to match."
        echo "  -c            Check for newer version of this script."
        echo "  -g <gitdir>   The directory where you keep your local"
        echo "                git repository."
        echo "  -i            Initialize an empty local git repository instead"
        echo "                of cloning an existing remote repository first."
        echo "  -l <localdir> The root directory where you keep your local"
        echo "                Slackware mirror; this directory contains"
        echo "                slackware-<RELEASE> or slackware64-<RELEASE>"
        echo "  -m <filename> Filename containing custom git commit message."
        echo "  -n            Only show the changes in the ChangeLog.txt"
        echo "                but don't sync anything and don't update git"
        echo "  -r <release>  Release ('$RELEASE' by default); use '-r 14.2'"
        echo "                if you work with a slackware 14.2 repository."
        echo "                Branch (-b) and release (-r) need to match."
        echo "  -q            Non-verbose output (for cron jobs)."
        echo "  -v            Verbose progress indications."
        exit
        ;;
    a ) ARCH=${OPTARG}
        ;;
    b ) BRANCH=${OPTARG}
        ;;
    c ) CHECKVER="yes"
        ;;
    f ) FORCE="yes"
        ;;
    g ) GITDIR=$(readlink -f ${OPTARG})
        ;;
    i ) INITNEW="yes"
        ;;
    l ) SLACKROOTDIR=$(readlink -f ${OPTARG})
        ;;
    m ) CUSTOM_COMMIT=$(readlink -f ${OPTARG})
        ;;
    n ) ONLYDIFF=1
        ;;
    q ) # No output at all if we are already in sync
        DEBUG=0
        ;;
    r ) RELEASE=${OPTARG}
        ;;
    v ) echo "Enabling verbose output...."
        DEBUG=$(($DEBUG + 1))
        ;;
    * ) echo "You passed an illegal switch ($Option) to the program!"
        echo "Run '$0 -h' for more help."
        exit
        ;;   # DEFAULT
  esac
done

# End of option parsing.
shift $(($OPTIND - 1))

#  $1 now references the first non option item supplied on the command line
#  if one exists.
# ---------------------------------------------------------------------------

# The suffix for the file where we keep the timestamps.
# Transforms 'current' to 'CURRENT' and '14.2 to '142':
LSUFF="$(echo ${RELEASE^^} | tr -d '.')"

# Check for an updated version of this script:
if [ "$CHECKVER" == "yes" ]; then
  if [ $DEBUG -ne 0 ]; then
    echo "#"
    echo "# Checking version of '${ORIGSCR}' ..."
    echo "#"
  fi
  CVRS=$(cat ${0} | grep 'Id: ' | head -1 | \
    sed -e 's/^.*Id: maintain_current_git.sh,v \([0-9.]*\) .*$/\1/')
  NVRS=$(wget -T 10 -q -O - ${ORIGSCR} | grep 'Id: ' | \
    head -1 | \
    sed -e 's/^.*Id: maintain_current_git.sh,v \([0-9.]*\) .*$/\1/')
  if [ -z "$CVRS" -o -z "$NVRS" ]; then
    echo "# Cannot compare version against the script's original;"
    echo "# Your script version reports '$CVRS', the original reports '$NVRS'"
    if [ -z "$NVRS" ]; then
      echo "# Possible cause is a failure to retrieve the remote script:"
      echo "#   '${ORIGSCR}'."
    fi
  elif [ "$CVRS" != "$NVRS" ]; then
    echo "# Your version of this script is '$CVRS', while version '$NVRS' is reported"
    echo "# by remote '${ORIGSCR}'"
  elif [ "$CVRS" == "$NVRS" ]; then
    echo "# You have the most recent version of this script"
  fi
fi

if [ "$ARCH" = "x86_64" ]; then
  SLACKRELEASE="${SLACKRELEASE:-${DISTRO}64-${RELEASE}}"
  PKGMAIN="${DISTRO}64"
else
  SLACKRELEASE="${SLACKRELEASE:-${DISTRO}-${RELEASE}}"
  PKGMAIN="${DISTRO}"
fi

if [ ! -d ${SLACKROOTDIR}/${SLACKRELEASE} ]; then
  echo "$(date) [$$]: Cannot find '${SLACKROOTDIR}/${SLACKRELEASE}' directory"
  echo "**  Aborting now..."
  exit 1
fi

if [ ! -d ${SLACKROOTDIR}/${SLACKRELEASE}/${PKGMAIN} ]; then
  echo "$(date) [$$]: Cannot find '${SLACKROOTDIR}/${SLACKRELEASE}/${PKGMAIN}' directory"
  echo "**  Did you specify an incorrect architecture ($ARCH)?"
  echo "**  Aborting now..."
  exit 1
fi

if [ ! -d ${GITDIR} ]; then
  if ! mkdir -p ${GITDIR} ; then
    echo "$(date) [$$]: Failed creating directory '${GITDIR}'!"
    echo "** Aborting now..."
    exit 1
  else
    if [ $DEBUG -ne 0 ]; then
      if [ "$INITNEW" = "yes" ]; then
        echo "$(date) [$$]: Creating empty git repository '${GITDIR}'..."
      else
        echo "$(date) [$$]: Cloning remote git repository '${GITURI}/${REPONAME}.git' into '${GITDIR}'..."
      fi
    fi
    cd  ${GITDIR}
      if [ "$INITNEW" = "yes" ]; then
        git init --quiet
      else
        git clone --quiet ${GITURI}/${REPONAME}.git .
      fi
      echo "Tracking updates in ${SLACKRELEASE}" > .git/description
      echo "LATEST_ADDITION_TO_${LSUFF}" > .gitignore
      cat <<EOT > .git/config 
[core]
        repositoryformatversion = 0
        filemode = true
        bare = false
        logallrefupdates = true
[remote "origin"]
        url = ${GITURI}/${REPONAME}.git
        fetch = +refs/heads/*:refs/remotes/origin/*
[branch "master"]
        remote = origin
        merge = refs/heads/master
[user]
        name = ${AUTHOR_NAME}
        email = ${AUTHOR_EMAIL}
EOT
      if [ "$INITNEW" = "no" ]; then
        if [ $DEBUG -ne 0 ]; then
          echo "$(date) [$$]: Fetching remote branches and tags from '${GITURI}/${REPONAME}.git'..."
        fi
        git branch -r | grep -v '\->' | \
          while read remote; do
            git branch -q --track "${remote#origin/}" "$remote" 2>/dev/null
          done
        git fetch -q --all
        git pull -q --all
        git fetch -q --tags
      fi
    cd - 1>/dev/null
  fi
fi

# We have made sure that there's a ${GITDIR} and if we had to create it,
# we also initialized a git repository. However, things can still be goofy:
if [ ! -d ${GITDIR}/.git ]; then
  echo "$(date) [$$]: Directory '${GITDIR}' doesn't contain a git repository!"
  echo "** Aborting now..."
  exit 1
else
  cd ${GITDIR}/.git
    if [ "$(git rev-parse --is-inside-git-dir)" = "false" ]; then
      echo "$(date) [$$]: Directory '${GITDIR}/.git' is not a git repository!"
      echo "** Aborting now..."
      exit 1
    fi
  cd - 1>/dev/null
fi

if [ $DEBUG -ge 3 ]; then
  VRSYNC="-v"
else
  VRSYNC=" "
fi

# ---------------------------------------------------------------------------

if [ -e $PIDFILE ]; then

  echo "**"
  echo "** Another instance ($(cat $PIDFILE)) still running?"
  echo "** If you are sure that no other instance is running, then you should"
  echo "** delete the lockfile '${PIDFILE}' and re-start this script."
  echo "** Aborting now..."
  echo "**"
  exit 1

else

  echo $$ > $PIDFILE

  umask 022

  # We have a git repository, but it may have multiple branches.
  if [ $DEBUG -ne 0 ]; then
    echo "$(date) [$$]: Checking out '${BRANCH}' branch"
  fi
  cd ${GITDIR}
    git checkout -q ${BRANCH} 2>/dev/null
    if [ -f ChangeLog.txt -a ! -f LATEST_ADDITION_TO_${LSUFF} ]; then
      if [ "$INITNEW" = "no" ]; then
        # If we just cloned the remote repository we need to create this
        # to prevent an empty check-in further down:
        if [ $DEBUG -ne 0 ]; then
          echo "$(date) [$$]: writing 'LATEST_ADDITION_TO_${LSUFF}'..."
        fi
        head -n 40 ChangeLog.txt | grep -Ei "^(mon|tue|wed|thu|fri|sat|sun)" \
          | head -n 1 > LATEST_ADDITION_TO_${LSUFF}
      fi
    fi
  cd - 1>/dev/null
  if ! grep -q LATEST_ADDITION_TO_${LSUFF} ${GITDIR}/.gitignore ; then
    echo "LATEST_ADDITION_TO_${LSUFF}" >> ${GITDIR}/.gitignore
  fi

  # First get the ChangeLog.txt
  # (no further action needed when there are no changes in this file):
  if [ $DEBUG -ne 0 ]; then
    echo "$(date) [$$]: Comparing ChangeLog.txt..."
  fi

  # Find the latest slackware-current update:
  LATEST_ADD=$(head -n 40 ${SLACKROOTDIR}/${SLACKRELEASE}/ChangeLog.txt|grep -Ei "^(mon|tue|wed|thu|fri|sat|sun)"|head -n 1)
  echo ${LATEST_ADD} > ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$

  # If ${GITDIR}/LATEST_ADDITION_TO_${LSUFF} doesn't exist, it might mean
  # that this is a first-time run. To prevent the script from aborting,
  # we create an empty file...
  if [ ! -e ${GITDIR}/LATEST_ADDITION_TO_${LSUFF} ]; then
    touch ${GITDIR}/LATEST_ADDITION_TO_${LSUFF}
  fi

  diff -b ${GITDIR}/LATEST_ADDITION_TO_${LSUFF} ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$ 1>/dev/null 2>/dev/null
  STATUS="$?"
  if [ "$STATUS" == "2" ]; then
    echo "$(date) [$$]: Trouble when running diff, aborting..."
    rm -f ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$
    rm -f $PIDFILE
    exit 1
  elif [ "$STATUS" == 0 ]; then
    [ $DEBUG -ne 0 ] && echo -n "$(date) [$$]: No difference found"
    if [ $FORCE == "yes" ]; then
      # we will continue as requested
      [ $DEBUG -ne 0 ] && echo ", continuing anyway..."
    else
      # quit the script now. 
      [ $DEBUG -ne 0 ] && echo ", exiting now...."
      rm -f ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$
      rm -f $PIDFILE
      exit 0
    fi
  else
    echo -n "$(date) [$$]: ${DISTRO^}-${RELEASE} ChangeLog.txt has updates"
    if [ $ONLYDIFF -eq 1 ]; then
      # quit the script now. 
      echo " (${LATEST_ADD}), that's all you wanted to know...."
      rm -f ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$
      rm -f $PIDFILE
      exit 0
    else
      # we will continue.
      echo ", committing updates to the tracking git repository."
    fi
  fi

  # Start the collection and update process:

  if [ -n "${CUSTOM_COMMIT}" ]; then
    # Custom commit text specified:
    COMMSG="${CUSTOM_COMMIT}"
    TAG_ADD=""
    TAG_REB=""
    TAG_REM=""
    TAG_UPD=""
    TAG_CRT=""
    TAG_MSG="${LATEST_ADD}"
  else
    # Save ChangeLog.txt update to file to use later as the git commit message:
    TMPFILE=$(mktemp -t curr2git.XXXXXX)
    echo -e "${LATEST_ADD}\n" > ${TMPFILE}
    sed -n '2,/+--------------------------+/p' "${SLACKROOTDIR}/${SLACKRELEASE}/ChangeLog.txt" | head -n -1 >> ${TMPFILE}
    COMMSG="${TMPFILE}"
    TAG_ADD="$(grep ': *Added.$' ${COMMSG} |wc -l)"
    TAG_REB="$(grep ': *Rebuilt.$' ${COMMSG} |wc -l)"
    TAG_REM="$(grep ': *Removed.$' ${COMMSG} |wc -l)"
    TAG_UPD="$(grep ': *Upgraded.$' ${COMMSG} |wc -l)"
    TAG_CRT="$(grep -F '(* Security fix *)' ${COMMSG} |wc -l)"
    TAG_MSG="${LATEST_ADD}: ${TAG_UPD} upgrades, ${TAG_REB} rebuilds, ${TAG_ADD} additions, ${TAG_REM} removals"
    if [ ${TAG_CRT} -ne 0 ]; then
      TAG_MSG="${TAG_MSG}, plus ${TAG_CRT} critical security fixes!"
    fi
  fi

  # Capture the timestamp of the latest addition:
  echo ${LATEST_ADD} > ${GITDIR}/LATEST_ADDITION_TO_${LSUFF}
  if [ $DEBUG -ge 1 ]; then
    echo "+--------------------------+"
    sed -n '2,/+--------------------------+/p' "${SLACKROOTDIR}/${SLACKRELEASE}/ChangeLog.txt"
  fi

  # Copy the files we want to keep under revision control:
  [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Syncing the changes in ${DISTRO^} to git-dir"
  rsync ${VRSYNC} --delete -rlptD \
    --exclude=".git*" --exclude=LATEST_ADDITION_TO_${LSUFF} \
    --exclude="*,v" \
    --exclude="*.tar.?z" --exclude="*.tar.bz2" --exclude="*.t?z" \
    --exclude="*.zip" \
    --exclude="CHECKSUMS*" --exclude="FILE_LIST" --exclude="FILELIST.txt" \
    --exclude="MANIFEST.**" --exclude="PACKAGES.TXT" \
    --exclude="*.bin" --exclude="*.img" --exclude="*.iso" --exclude="*.dsk" \
    --exclude="*.rpm" \
    --exclude="*.efi" --exclude="*.EFI" --exclude="*.s" \
    --exclude="*.exe" --exclude="*.EXE" \
    --exclude="*.exe.?z" --exclude="*.EXE.?z" \
    --exclude="*.asc" --exclude="*.sig" --exclude="*.sign" --exclude="*.pgp" \
    --exclude="*.md5 --exclude=*.sha256 --exclude=*.sha512" \
    ${SLACKROOTDIR}/${SLACKRELEASE}/ ${GITDIR}/

  # Extract the package preload tarballs (_<pkgnam>.tar.gz in the sourcedir):
  cd ${SLACKROOTDIR}/${SLACKRELEASE}
    MYDIRLIST="source"
    for MYDIR in extra/source pasture/source testing/source ; do
      if [ -d "$MYDIR" ] ; then
        MYDIRLIST="$MYDIRLIST $MYDIR"
      fi
    done
    find ${MYDIRLIST} -type f -name "_*.tar.gz" \
    | while read TBALL ; do
      TDIR="$(dirname $TBALL)"
      TBASE="$(basename $TBALL .tar.gz)"
      if [ -z "$TDIR" -o -z "$TBASE" ]; then continue; fi
      rm -rf ${GITDIR}/${TDIR}/${TBASE} 2>/dev/null
      mkdir -p ${GITDIR}/${TDIR}/${TBASE}
      tar \
        -C ${GITDIR}/${TDIR}/${TBASE} \
        -xf ${SLACKROOTDIR}/${SLACKRELEASE}/${TBALL} \
        2>/dev/null  # Silence the 'mknod' warnings
      if [ $? -ne 0 ]; then
        # Non-zero exit code means the tarball contains device nodes.
        # Generate script which will re-create device-nodes (requires root):
        mknodescript ${TBALL} \
          ${GITDIR}/${TDIR}/${TBASE}/dev/mknodes.sh
      fi
    done
  cd - 1>/dev/null

  # Extract the skeleton initrd if we find one.
  # Note that device files in /dev/ cannot be extracted from the skeleton,
  # since that would require root (to run 'mknod').
  # Instead we will generate a script from the tarball that recreates them.
  INITRD=$(find ${SLACKROOTDIR}/${SLACKRELEASE}/source -name skeleton_initrd.tar.gz)
  if [ -n "${INITRD}" ]; then
    [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Extracting skeleton initrd"
    rm -rf ${GITDIR}/source/installer/sources/initrd
    mkdir -p ${GITDIR}/source/installer/sources/initrd
    tar -C ${GITDIR}/source/installer/sources/initrd --exclude=dev/* \
      -xf ${INITRD}
    tar -C ${GITDIR}/source/installer/sources/initrd  --occurrence=1 \
      -xf ${INITRD} dev/makedevs.sh
    # Generate script which will re-create the device-nodes (requires root):
    mknodescript ${INITRD} \
      ${GITDIR}/source/installer/sources/initrd/dev/mknodes.sh
  fi

  # Clean out unwanted files (i.e. txt files in the package directories):
  [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Cleaning out any unwanted files"
  rm -f ${GITDIR}/${PKGMAIN}/*/*.txt
  rm -f ${GITDIR}/extra/*/*.txt
  rm -f ${GITDIR}/pasture/*.txt
  rm -f ${GITDIR}/testing/packages/*.txt
  rm -f ${GITDIR}/testing/packages/*/*.txt

  # Remove empty directories:
  find ${GITDIR} -type d -depth | grep -v .git \
    | xargs rmdir --ignore-fail-on-non-empty 2>/dev/null

  # Create a script the user can run to re-compress all the files
  # that we are going to de-compress shortly:
  cat <<EOT > ${GITDIR}/recompress.sh
#!/bin/sh
#
# Run this script in the root directory of the repository to re-compress
# all patches and scripts that were un-compressed to make a better git commit:
#
EOT
  ( cd ${GITDIR}
    find . -type f -name "*.gz" | sed -e 's/^/gzip /' -e 's/.gz$//' \
      >> ${GITDIR}/recompress.sh
    find . -type f -name "*.lz" | sed -e 's/^/lzip /' -e 's/.lz$//' \
      >> ${GITDIR}/recompress.sh
    find . -type f -name "*.bz2" | sed -e 's/^/bzip2 /' -e 's/.bz2$//' \
      >> ${GITDIR}/recompress.sh
  )
  chmod +x ${GITDIR}/recompress.sh

  # Generate the list of compressed files we need to decompress:
  [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Decompressing files before commit"
  find ${GITDIR} -type f \
    \( -name "*.?z" -o -name "*.bz2" \) \
    -print0 \
    | xargs -0 -r -I FILE bash -c "_decompress FILE"

  # Generate the RSS file:
  [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Generating RSS file in git"
  rss_changelog ${GITDIR}

  # And finally, the actual git commit:
  [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Committing updates to git"
  ( cd ${GITDIR}
    git add -A .
    GIT_COMMITTER_NAME="${COMMITTER_NAME}" \
    GIT_COMMITTER_EMAIL="${COMMITTER_EMAIL}" \
    git commit --quiet -F ${COMMSG} \
      --author="${AUTHOR_NAME} <${AUTHOR_EMAIL}>" \
      --date="${LATEST_ADD}"
    # Tag the commit so we can point to it with a clean URL:
    if [ "${BRANCH}" = "master" ]; then
      TAG_NAME="$(LC_ALL=C TZ=UTC date -d "${LATEST_ADD}" +%Y%m%d%H%M%S)"
    else
      TAG_NAME="$(LC_ALL=C TZ=UTC date -d "${LATEST_ADD}" +%Y%m%d%H%M%S)_${BRANCH}"
    fi
    if ! git tag | grep -q ${TAG_NAME} ; then
      # Tag does not yet exist:
      GIT_COMMITTER_NAME="${COMMITTER_NAME}" \
      GIT_COMMITTER_EMAIL="${COMMITTER_EMAIL}" \
      GIT_COMMITTER_DATE="${LATEST_ADD}" \
      git tag -m "${TAG_MSG}" "${TAG_NAME}"
    fi
  )

  # You can define the function 'post_current()' in the configuration file
  # 'maintain_current_git.conf'.  It will then be executed here.
  # Employ this for instance to push your local commits to a remote:
  if type post_current 1>/dev/null 2>/dev/null ; then
    [ $DEBUG -ne 0 ] && echo "$(date) [$$]: Running custom function 'post_current'"
    post_current
  fi

  echo "$(date) [$$]: Done!"

  # Cleanup:
  rm -f $PIDFILE
  rm -f ${TMP}/LATEST_ADDITION_TO_${LSUFF}_$$
  rm -f ${TMPFILE}
fi

