Skip to content

Instantly share code, notes, and snippets.

@mjhennig
Created June 8, 2017 13:11
Show Gist options
  • Select an option

  • Save mjhennig/a1c533ab235b0d1495b8abc50c901b0b to your computer and use it in GitHub Desktop.

Select an option

Save mjhennig/a1c533ab235b0d1495b8abc50c901b0b to your computer and use it in GitHub Desktop.
Combining Git and Mercurial using a custom login shell
#!/bin/sh
# This script is part of the Uplink project. It is used as login-shell for
# SSH users git@ and hg@ in development, allowing for integration of both Git
# and Mercurial workflows, and for the user to push into any repository that
# is identified by a valid name, irregardless whether it exists already.
basename="`basename \"$0\"`"
# Login shells are meant to be executed with option -c followed by a command
if [ $# -ne 2 -o "x-c" != "x$1" ]; then
echo "$basename: Invalid or malformed login shell command: $@" >&2
exit 1
fi
# Environment variable $SSH_ORIGINAL_COMMAND is required by hg-ssh(8) only,
# but made available to the entire process, including any Git triggers:
SSH_ORIGINAL_COMMAND="$2"
export SSH_ORIGINAL_COMMAND
# Expand the parameter list according to the given command:
set -e -- $2
# Determine which SCM the client is using:
case "$USER" in
*hg*|*mercurial*) # https://www.mercurial-scm.org/wiki/SharedSSH#hg-ssh
if [ $# -eq 5 -a "# $1 $2 ... $4 $5" = "# hg -R ... serve --stdio" ]; then
backend="hg-ssh"
convert="push"
repository="$3"
set -- "$repository"
else
echo "$basename: Malformed or unrecognized Mercurial command: $@" >&2
exit 1
fi;;
*git*) # https://git-scm.com/docs/git-shell
if [ $# -eq 2 -a "# ${1%%-*}-command ..." = "# git-command ..." ]; then
backend="git-shell"
convert="pull"
repository=${2%"'"}; repository=${repository#"'"}
set -- -c "$1 '$repository/.git'"
else
echo "$basename: Malformed or unrecognized Git command: $@" >&2
exit 1
fi;;
*) echo "$basename: Try Git or Mercurial instead of: $@" >&2
exit 1
;;
esac
# Ensure the repository name is neither empty nor contains any path fragments:
case "$repository" in */*|*.*|'')
echo "$basename: Invalid repository name: '$repository'" >&2
exit 1;;
esac
# The directory associated with the repository hosts not only the Mercurial
# and Git resources, but also the lock file for remote operations, hence it
# must exist even before the repositories are crated on-demand (see below)
mkdir -p "$repository"
# All interaction with the repository is done in transaction scope: After
# aquiring a lock on the repository, execution is delegated to either the
# Git or Mercurial handler, before finally synchronizing the repositories
# and releasing the lock:
( if ! flock -n 9; then
echo "$basename: Encountered lock on \"$repository\"" >&2
exit 1
fi
# Ensure the Mercurial repository has been initialized
test -d "$repository/.hg" || hg init -q "$repository" >&2
# Ensure the Git repository has been initialized
test -d "$repository/.git" || git init -q --bare "$repository/.git" >&2
# Invoke the "original" SCM shell handler:
$backend "$@"
# Perform the subsequent synchronization via hg-git pull or push:
hg -R "$repository" $convert -q --force "$repository/.git" >&2
) 9>"$repository/.lock"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment