#!/bin/bash set -o errexit # for use with git-remote-hg: # http://felipec.wordpress.com/2012/11/13/git-remote-hg-bzr-2/ # Uncomment to have verbose debug output #debug=1 PROJRC_COUNT=0 SUBPATHS_COUNT=0 print_usage() { echo "$(basename $0) -- HG subrepository importer for git-remote-hg" echo "Usage: $(basename $0) [OPTIONS...]" } # We parse the projrc file in the assembly (on the server), to check # for any projrc substitutions. # # Arg 1: repository (path and name) # # Return the substitution key/values in arrays: # PROJRC_KEY # PROJRC_VAL # PROJRC_COUNT will be greater than zero if any substitutions were found. hg_parse_projrc() { if [ ${#} -lt 1 ]; then echo "Missing repository name" fi # Repo name can have spaces in it, hence the use of ${*} local repo="${*}" if [ -f "${repo}/.hg/projrc" ]; then TMPF="/tmp/$(basename $0).projrc-$$.tmp" # Create temporary copy to replace backslashes with slashes. # Make sure to only replace path backslashes, not backslashes for # quoting spaces. # \\[^ ] Match a backslash, not followed by a space cat "${repo}/.hg/projrc" | sed 's/\\\([^ ]\)/\/\1/g' > ${TMPF} while read p; do if echo ${p} | grep -q "\s=\s"; then local key=$(echo ${p} | sed 's/^\(.*\) = .*/\1/') local val=$(echo ${p} | sed 's/.* = \(.*\)/\1/') PROJRC_KEY[${PROJRC_COUNT}]="${key}" PROJRC_VAL[${PROJRC_COUNT}]="${val}" if [ -n "${debug}" ]; then echo "Parsing PROJRC entry: ${p}" echo " key: ${key}" echo " val: ${val}" fi PROJRC_COUNT=$((PROJRC_COUNT + 1)) fi done < ${TMPF} rm ${TMPF} fi } # We parse the Mercurial configuration, to check # for any SUBPATHS substitutions. # # Return the substitution key/values in arrays: # SUBPATHS_KEY # SUBPATHS_VAL # SUBPATHS_COUNT will be greater than zero if any substitutions were found. hg_parse_subpaths() { subpaths=$(hg showconfig subpaths) if [ -n "${subpaths}" ]; then for p in ${subpaths}; do local key=$(echo ${p} | sed 's/^subpaths\.\(.*\)=.*/\1/') local val=$(echo ${p} | sed 's/.*=\(.*\)/\1/') # Replace '\\' with '/' key=${key//\\\\/\/} # ^^ replace multiple times val=${val//\\\\/\/} SUBPATHS_KEY[${SUBPATHS_COUNT}]="${key}" SUBPATHS_VAL[${SUBPATHS_COUNT}]="${val}" if [ -n "${debug}" ]; then echo "Parsing SUBPATHS entry: ${p}" echo " key: ${key}" echo " val: ${val}" fi SUBPATHS_COUNT=$((SUBPATHS_COUNT + 1)) done fi } # Replace the given path with either entries from PROJRC or SUBPATHS # Arg 1: path apply_substitutions() { # Repo name can have spaces in it, hence the use of ${*} local path="${*}" if [ ${PROJRC_COUNT} -ne 0 ]; then for s in $(seq 0 $((${PROJRC_COUNT} - 1))); do local pattern=${PROJRC_KEY[${s}]} local replacement=${PROJRC_VAL[${s}]} path=${path/${pattern}/${replacement}} done fi if [ ${SUBPATHS_COUNT} -ne 0 ]; then for s in $(seq 0 $((${SUBPATHS_COUNT} - 1))); do local pattern=${SUBPATHS_KEY[${s}]} local replacement=${SUBPATHS_VAL[${s}]} path=${path/${pattern}/${replacement}} done fi echo "${path}" } # Map a revision to a branch name in HG subrepository # Use hg log (in original repo) to get branch name corresponding to that # revision. # # Arg 1: subrepository URL # Arg 2: id # Arg 3: revision subrepo_find_branch() { local src="${1}" local id="${2}" local rev="${3}" pushd "${src}" 1> /dev/null branch=$(hg log -r ${rev} | grep "branch:" | sed "s/branch:\ *//") if [ -z "${branch}" ]; then # If "branch:" is null, this indicate we are on the default branch branch=default fi num=$(hg log --branch "${branch}" --template '{node}\n' | \ grep -n ${rev} | awk -F ':' '{print $1}') if [ -n "${debug}" ]; then echo " branch: ${branch}" echo " num: ${num}" fi popd 1> /dev/null } if [ "x${1}" = "x--help" ]; then print_usage exit 1 fi if [ ! -f .hgsub ]; then echo "No Mercurial subrepositories found" exit 1 fi if [ ! -f .gitignore ]; then # We do not want to track .gitignore itself echo ".gitignore" > .gitignore fi asm=$(cat .git/config | grep "hg::" | sed "s/.*url = hg::\(.*\)/\1/") hg_parse_projrc ${asm} hg_parse_subpaths HGSUB_TMP="/tmp/$(basename $0).hgsub-$$.tmp" # Create temporary copy to replace backslashes with slashes. cat .hgsub | sed 's/\\/\//g' > ${HGSUB_TMP} # Read lines from .hgsub while read sub; do # Remove CR (DOS) sub="${sub//$'\r'/}" if [ "${sub}" != "" ]; then # Get subrepository URL src="${sub//*= /}" # Replace using Mercurial subpaths substitutions (either projrc or from hgrc) src=$(apply_substitutions ${src}) # Original name repo=$(basename "${src}") srcpath=$(dirname "${src}") # Get subrepository local alias or label dest="${sub// =*}" # Get project ID (example: S0289) id=$(echo ${sub} | sed "s/.*\(S[0-9][0-9][0-9][0-9]\).*/\1/") # Get revision of subrepository (remove CR from .hgsubstate) rev=$(cat .hgsubstate | tr -d '\r' | grep "${id}" | sed "s/ .*//") if [ -n "${debug}" ]; then echo "repo: ${repo}" echo " path: ${srcpath}" echo " id: ${id}" echo " local: ${dest}" echo " rev: ${rev}" fi if [ ! -d "${dest}" ]; then subrepo_find_branch "${src}" ${id} ${rev} # git-remote-hg seems to replace # "BRANCH - NAME" # with # "BRANCH___-___NAME" branch="${branch// /___}" if [ -n "${debug}" ]; then echo "branch: ${branch}" fi git clone "hg::${src}" "${dest}" cd "${dest}" if [ -x ${HOME}/scripts/git-set-local-author.sh ]; then # Make sure commits have correct author for LSI ${HOME}/scripts/git-set-local-author.sh fi if [ "x${branch}" != "xdefault" ]; then # Make tracking branch git checkout -f "branches/${branch}" # Adjusting git tree to specific commit specified in # .hgsubstate: # The SHA from hg and git are not the same, therefore, we must # find the commit sequence in hg and map this # to a hash in git using the ${num} variable: git_rev=$(git log --oneline | sed -n "${num}p" | awk '{print $1}') git reset --hard ${git_rev} fi cd .. if ! grep -q "${dest}" .gitignore ; then # Ignore subrepo in top-level git repository echo "${dest}" >> .gitignore fi fi fi done < ${HGSUB_TMP} rm ${HGSUB_TMP}