#!/bin/bash
# dev/vcs - Version Control System (VCS) Support
#
# This library provides an abstract interface to various kinds of
# version control systems, unifying them into a set of git repositories.

set -e


######
# Generic VCS Settings

dev_vcs_config_init() {
	# $vcs_repo_kinds[] - Kinds of repositories presently supported.
	#
	# .. note:
	#    This could be moved to a plugin-based system.
	lib_setting_arrays -ro vcs_repo_kinds
	vcs_repo_kinds=( git svn hg bzr file www )
}


######
# VCS Package Specification
#
# A `VCS package specification` is encoded as the following tuple::
#
#   <name>|<kind>|<url>
#
# where:
#   * ``name`` is the local path name for the project file,
#   * ``kind`` is the kind of VCS to use (see ``$vcs_repo_kinds[]``), and
#   * ``url`` is the URL for retrieving the package.

# vcs_spec_build() - Prints a project specification for the named
# project ($1), repository kind ($2), and URL ($3).
vcs_spec_build() {
	has_args 3 "$@"
	echo "$1|$2|$3"
}

# with_vcs_spec() - Decomposes the given specification ($1) into its
# parts, passing them to the given command ($@).
with_vcs_spec() {
	local spec=$1
	shift

	local -a args
	args=( $(str_split '|' "$spec") )
	"$@" "${args[@]}"
}

# vcs_spec_info() - Prints a human-readable version of the parts of a
# package specification ($1)
vcs_spec_info() { for_each _vcs_spec_info "$@"; }
_vcs_spec_info() { with_vcs_spec "$1" __vcs_spec_info; }
__vcs_spec_info() { echo "$1 ($2)"; echo -e "\t$3"; }

# vcs_spec_clone() - Clones project repositories from specifications ($@).
vcs_spec_clone() { for_each _vcs_spec_clone "$@"; }
_vcs_spec_clone() { with_vcs_spec "$1" vcs_repo_clone; }

# vcs_spec_update() - Updates project repositories from specifications ($@).
vcs_spec_update() { for_each _vcs_spec_clone "$@"; }
_vcs_spec_update() { with_vcs_spec "$1" __vcs_spec_update; }
__vcs_spec_update() { run_in_dir "$1" vcs_repo_update "$2"; }


######
# Generic Version Control

vcs_run() {
	min_args 2 "$@"
	local kind=$1
	local cmd=$2
	shift 2

	local sym=$kind
	case "$kind" in
	(git) lib_load 'sys/tool/git' ;;
	(git-svn) lib_load 'sys/tool/git-svn' ;;
	(svn) lib_load 'sys/tool/svn' ;;
	(hg|bzr|file|www)
		error "$kind: backend unimplemented" ;;
	(*)
		error "$kind: unsupported repository" ;;
	esac
	sym=$(lib_symbol "$sym")
	"${sym}_${cmd}" "$@"
}


######
# Project Management

vcs_repo_kind() {
	if [ -d .git ]; then
		echo 'git'
	elif [ -d .svn ]; then
		echo 'svn'
	else
		error "unknown repository kind"
	fi
}

git_vcs_url() { git remote get-url origin; }
svn_vcs_url() { svn info |grep '^URL:' | cut -c6-; }

vcs_repo_url() {
	local kind=$1
	kind=${kind:-$(vcs_repo_kind)}
	vcs_run "$kind" vcs_url
}

# vcs_repo_spec() - Prints a project specification for the named repository
# ($1), extracting the repository kind and URL from the current directory.
# $1 - Repository name
vcs_repo_spec() {
	local name=$1
	local kind url
	kind=$(vcs_repo_kind)
	url=$(vcs_repo_url "$kind")
	vcs_spec_build "$name" "$kind" "$url"
}


# vcs_repo_clone() - Creates a local copy of a repository ($1) given its
# type ($2) and URL ($3).
vcs_repo_clone() {
	has_args 3 "$@"
	local name=$1
	local kind=$2
	local url=$3

	if [ ! -d "$name" ]; then
		vcs_run "$kind" clone "$url" "$name"
		return
	fi

	local spec rspec
	spec=$(vcs_spec_build "$@")
	rspec=$(run_in_dir "$name" vcs_repo_spec "$name")

	if [ "$spec" = "$rspec" ]; then
		run_in_dir "$name" vcs_repo_update "$kind"
	else
		error "$name: specification changed\n$spec\n$rspec"
	fi
}

vcs_repo_update() {
	local kind=$1
	kind=${kind:-$(vcs_repo_kind)}
	vcs_run "$kind" update
}

View the Developer Guide Index

View the Reference Manual Index


Generated on Fri Jul 28 14:35:31 PDT 2017 by mcsh d14 v0.23.0.