#!/bin/sh

# Copyright (c) 2007, 2008 Rocco Rutte <pdmef@gmx.net> and others.
# License: MIT <http://www.opensource.org/licenses/mit-license.php>

READLINK="readlink"
if command -v greadlink > /dev/null; then
  READLINK="greadlink" # Prefer greadlink over readlink
fi

if ! $READLINK -f "$(which "$0")" > /dev/null 2>&1 ; then
    ROOT="$(dirname "$(which "$0")")"
    if [ ! -f "$ROOT/hg-fast-export.py" ] ; then
	echo "hg-fast-exports requires a readlink implementation which knows" \
	     " how to canonicalize paths in order to be called via a symlink."
	exit 1
    fi
else
    ROOT="$(dirname "$($READLINK -f "$(which "$0")")")"
fi

REPO=""
PFX="hg2git"
SFX_MAPPING="mapping"
SFX_MARKS="marks"
SFX_HEADS="heads"
SFX_STATE="state"
GFI_OPTS=""

if [ -z "${PYTHON}" ]; then
    # $PYTHON is not set, so we try to find a working python with mercurial:
    for python_cmd in python3 python; do
        if command -v $python_cmd > /dev/null; then
            $python_cmd -c 'from mercurial.scmutil import revsymbol' 2> /dev/null
            if [ $? -eq 0 ]; then
                PYTHON=$python_cmd
                break
            fi
        fi
    done
fi
if [ -z "${PYTHON}" ]; then
    echo "Could not find a python interpreter with the mercurial module >= 4.6 available. " \
        "Please use the 'PYTHON' environment variable to specify the interpreter to use."
    exit 1
fi

"${PYTHON}" -c 'import sys; exit(sys.version_info.major==3 and sys.version_info.minor >= 7)'

if [ $? -eq 0 ]; then
    echo "Could not find an interpreter for a supported Python version (>= 3.7)" \
        "Please use the 'PYTHON' environment variable to specify the interpreter to use."
    exit 1
fi

USAGE="[--quiet] [-r <repo>] [--force] [--ignore-unnamed-heads] [-m <max>] [-s] [--hgtags] [-A <file>] [-B <file>] [-T <file>] [-M <name>] [-o <name>] [--hg-hash] [-e <encoding>]"
LONG_USAGE="Import hg repository <repo> up to either tip or <max>
If <repo> is omitted, use last hg repository as obtained from state file,
GIT_DIR/$PFX-$SFX_STATE by default.

Note: The argument order matters.

Options:
	--quiet   Passed to git-fast-import(1)
	-r <repo> Mercurial repository to import
	--force   Ignore validation errors when converting, and pass --force
	          to git-fast-import(1)
	-m <max>  Maximum revision to import
	-s        Enable parsing Signed-off-by lines
	--hgtags  Enable exporting .hgtags files
	-A <file> Read author map from file
	          (Same as in git-svnimport(1) and git-cvsimport(1))
	-B <file> Read branch map from file
	-T <file> Read tags map from file
	-M <name> Set the default branch name (defaults to 'master')
	-n        Do not perform built-in (broken in many cases) sanitizing
                  of branch/tag names.
	-o <name> Use <name> as branch namespace to track upstream (eg 'origin')
	--hg-hash Annotate commits with the hg hash as git notes in the
                  hg namespace.
	-e <encoding> Assume commit and author strings retrieved from 
	              Mercurial are encoded in <encoding>
	--fe <filename_encoding> Assume filenames from Mercurial are encoded 
	                         in <filename_encoding>
	--mappings-are-raw Assume mappings are raw <key>=<value> lines
	--filter-contents <cmd>  Pipe contents of each exported file through <cmd>
	                         with <file-path> <hg-hash> <is-binary> as arguments
	--plugin <plugin=init>  Add a plugin with the given init string (repeatable)
	--plugin-path <plugin-path> Add an additional plugin lookup path
"
case "$1" in
    -h|--help)
      echo "usage: $(basename "$0") $USAGE"
      echo ""
      echo "$LONG_USAGE"
      exit 0
      ;;

    --debug)
      echo -n "Using Python: "
      "${PYTHON}" --version
      echo -n "Using Mercurial: "
      hg --version
      exit 0
esac

IS_BARE=$(git rev-parse --is-bare-repository) \
    || (echo "Could not find git repo" ; exit 1)
if test "z$IS_BARE" != ztrue; then
   # This is not a bare repo, cd to the toplevel
   TOPLEVEL=$(git rev-parse --show-toplevel) \
       || (echo "Could not find git repo toplevel" ; exit 1)
   cd "$TOPLEVEL" || exit 1
fi
GIT_DIR=$(git rev-parse --git-dir) || (echo "Could not find git repo" ; exit 1)


IGNORECASEWARN=""
IGNORECASE=`git config core.ignoreCase`
if [ "true" = "$IGNORECASE" ]; then
    IGNORECASEWARN="true"
fi;


while case "$#" in 0) break ;; esac
do
  case "$1" in
    -r|--r|--re|--rep|--repo)
      shift
      REPO="$1"
      ;;
    --q|--qu|--qui|--quie|--quiet)
      GFI_OPTS="$GFI_OPTS --quiet"
      ;;
    --force)
      # pass --force to git-fast-import and hg-fast-export.py
      GFI_OPTS="$GFI_OPTS --force"
      IGNORECASEWARN="";
      break
      ;;
    -*)
      # pass any other options down to hg2git.py
      break
      ;;
    *)
      break
      ;;
  esac
  shift
done

if [ ! -z "$IGNORECASEWARN" ]; then
    echo "Error: The option core.ignoreCase is set to true in the git"
    echo "repository. This will produce empty changesets for renames that just"
    echo "change the case of the file name."
    echo "Use --force to skip this check or change the option with"
    echo "git config core.ignoreCase false"
    exit 1
fi;

# Make a backup copy of each state file
for i in $SFX_STATE $SFX_MARKS $SFX_MAPPING $SFX_HEADS ; do
    if [ -f "$GIT_DIR/$PFX-$i" ] ; then
	cp "$GIT_DIR/$PFX-$i" "$GIT_DIR/$PFX-$i~"
    fi
done

# for convenience: get default repo from state file
if [ x"$REPO" = x -a -f "$GIT_DIR/$PFX-$SFX_STATE" ] ; then
  REPO="`grep '^:repo ' "$GIT_DIR/$PFX-$SFX_STATE" | cut -d ' ' -f 2`"
  echo "Using last hg repository \"$REPO\""
fi

if [  -z "$REPO" ]; then
    echo "no repo given, use -r flag"
    exit 1
fi

# make sure we have a marks cache
if [ ! -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
  touch "$GIT_DIR/$PFX-$SFX_MARKS"
fi

# cleanup on exit
trap 'rm -f "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp"' 0

_err1=
_err2=
exec 3>&1
{ read -r _err1 || :; read -r _err2 || :; } <<-EOT
$(
  exec 4>&3 3>&1 1>&4 4>&-
  {
    _e1=0
    GIT_DIR="$GIT_DIR" "$PYTHON" "$ROOT/hg-fast-export.py" \
      --repo "$REPO" \
      --marks "$GIT_DIR/$PFX-$SFX_MARKS" \
      --mapping "$GIT_DIR/$PFX-$SFX_MAPPING" \
      --heads "$GIT_DIR/$PFX-$SFX_HEADS" \
      --status "$GIT_DIR/$PFX-$SFX_STATE" \
      "$@" 3>&- || _e1=$?
    echo $_e1 >&3
  } | \
  {
    _e2=0
    git fast-import $GFI_OPTS --export-marks="$GIT_DIR/$PFX-$SFX_MARKS.tmp" 3>&- || _e2=$?
    echo $_e2 >&3
  }
)
EOT
exec 3>&-
[ "$_err1" = 0 -a "$_err2" = 0 ] || exit 1

# move recent marks cache out of the way...
if [ -f "$GIT_DIR/$PFX-$SFX_MARKS" ] ; then
  mv "$GIT_DIR/$PFX-$SFX_MARKS" "$GIT_DIR/$PFX-$SFX_MARKS.old"
else
  touch "$GIT_DIR/$PFX-$SFX_MARKS.old"
fi

# ...to create a new merged one
cat "$GIT_DIR/$PFX-$SFX_MARKS.old" "$GIT_DIR/$PFX-$SFX_MARKS.tmp" \
| uniq > "$GIT_DIR/$PFX-$SFX_MARKS"

# save SHA1s of current heads for incremental imports
# and connectivity (plus sanity checking)
for head in `git branch | sed 's#^..##'` ; do
  id="`git rev-parse refs/heads/$head`"
  echo ":$head $id"
done > "$GIT_DIR/$PFX-$SFX_HEADS"

# check diff with color:
# ( for i in `find . -type f | grep -v '\.git'` ; do diff -u $i $REPO/$i ; done | cdiff ) | less -r
