#!/bin/rc

# This script was written by David du Colombier in November 2014.
# History: https://blog.gopheracademy.com/advent-2014/wrapping-git/
#
# This script is regularly updated. The latest version is available on:
# http://9legacy.org/9legacy/tools/git
#
# Version 2022-11-28

rfork en

tmp=/tmp/git.$pid

fn usage{
	echo 'usage: git <command> [<args>]' >[1=2]
	exit usage
}

# supported commands:
# git init [options]
# git checkout [options] [<commit>]
# git clone [options] <repository> [<directory>]
# git pull [options] [<repository>
# git reset [options] [<commit>]

verbose=1

fn dprint{
	if(~ $verbose '1')
		echo $* >> $tmp/log
}

fn dircp{
	switch($#*){
	case 2
		@{cd $1 && tar cif $tmp/dircp.tar .}
		@{cd $2 && tar xf $tmp/dircp.tar}
	case *
		echo usage: dircp from to >[1=2]
		exit usage
	}
}

fn get{
	timeout=1800
	elapsed=0
	delay=0
	stride=10
	done=''

	while(test -z $done){
		hget $* >[2]/dev/null
		s=$status
		if(~ $s *'Not found on server'){
			if(test $elapsed -gt $timeout)
				done=1
			delay=`{echo $delay+$stride | bc}
			sleep $delay
			elapsed=`{echo $elapsed+$delay | bc}
		}
		if not
			done=1
	}

	status=$s
}
	

fn parse_local{
	src=$1

	dprint parse_src $src

	if(~ $src '')
		exit args

	src=`{echo $src | sed 's,/+$,,'}
	repo=`{basename $src}

	fetch_fn=fetch_local
}

fn parse_uri{
	src=$1

	dprint parse_src $src

	if(~ $src '')
		exit args

	website=`{echo $src | sed 's,https?://([^/]+).*,\1,'}
	if(~ $website ''){
		echo 'fatal: repository '''$src''' doesn''t exist' >[1=2]
		exit fatal
	}

	switch($website){
	case *.googlesource.com
		repo=`{echo $src | sed 's,https?://([^/]+)/([^/]+)/?,\2,'}
		if(~ $repo ''){
			echo 'fatal: repository '''$src''' doesn''t exist' >[1=2]
			exit fatal
		}
	case github.com
		user=`{echo $src | sed 's,https?://([^/]+)/([^/]+)/([^/]+)/?,\2,'}
		repo=`{echo $src | sed 's,https?://([^/]+)/([^/]+)/([^/]+)/?,\3,'}
		if(~ $user '' || ~ $repo ''){
			echo 'fatal: repository '''$src''' doesn''t exist' >[1=2]
			exit fatal
		}
	case gopkg.in
		repo=`{echo $src | sed 's,https?://([^/]+)/([^/]+)/?,\2,' | sed 's/\.v[0-9]+$//'}
		switch($repo){
		case inf
			user=go-inf
		case *
			echo 'fatal: repository '''$src''' isn''t supported' >[1=2]
			exit fatal
		}
		if(~ $user '' || ~ $repo ''){
			echo 'fatal: repository '''$src''' doesn''t exist' >[1=2]
			exit fatal
		}
	case *
		echo 'fatal: website '''$website''' isn''t supported' >[1=2]
		exit fatal
	}

	src=`{echo $src | sed 's,/+$,,'}

	fetch_fn=fetch_uri
}

fn parse_repository{
	repository=$1

	if(~ $repository '')
		exit args

	switch($repository){
	case http*://*
		parse_uri $1
	case /*
		parse_local $1
	case
		echo 'fatal: Unable to find remote helper for '''$repository'''' >[1=2]
		exit fatal
	}
}

fn gitconfig{
	uri=$1
	dst=$2

	if(~ $uri '' || ~ $dst '')
		exit args

	# if it wasn't copied from a local directory (alongside with the .git)
	if(! test -d $dst/.git){
		mkdir $dst/.git
		echo 'url = '^$uri >$dst/.git/config
	}
}

fn clone{
	target=master

	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case -b
			target = $1
			shift
		case *
			dprint option $opt
		}
	}

	parse_repository $1

	if(test $#* -eq 1)
		dst=$repo
	if not
		dst=$2

	if(test -d $dst){
		n=`{ls $dst | wc -l | awk '{print $1}'}
		if(! ~ $n 0){
			echo 'fatal: destination path '''$dst''' already exists and is not an empty directory.' >[1=2]
			exit fatal
		}
	}

	$fetch_fn $src $dst $target
	gitconfig $src $dst
}

fn cleanup{
	# paranoia
	if(! test -d .git || ! test -f .git/config){
		echo 'fatal: Not a git repository.' >[1=2]
		exit fatal
	}

	for(i in *){
		if(! ~ $i '.git'){
			if(test -f $i)
				rm $i
			if not if(test -d $i)
				rm -rf $i
		}
	}
}

fn fetch_local_master{
	src=$1
	dst=$2

	dprint fetch_local_master $src $dst

	if(~ $src '' || ~ $dst '')
		exit args

	if(! test -d $src){
		echo 'abort: repository '$src' not found!' >[1=2]
		exit fatal
	}

	if(! test -d $dst)
		mkdir -p $dst

	dircp $src $dst || exit
}

# This function is only called on a checkout when the
# .git/config contains an url= to another directory.
# This is not really useful in practice, because the .git/config
# is always copied from the original remote url on a clone.
fn fetch_local_object{
	src=$1
	dst=$2
	object=$3

	dprint fetch_local_object $src $dst $object

	if(~ $src '' || ~ $dst '' || ~ $object '')
		exit args

	if(! test -d $src || ! test -d $src/.git || ! test -f $src/.git/config){
		echo 'abort: repository '$src' not found!' >[1=2]
		exit fatal
	}

	uri=`{cat $src/.git/config | grep 'url ?= ?' | awk -F'=' '{print $2}'}

	fetch_uri $uri $dst $object
}

fn fetch_local{
	src=$1
	dst=$2
	object=$3

	if(~ $object '' || ~ $object master)
		fetch_local_master $src $dst
	if not
		fetch_local_object $src $dst $object
}

fn fetch_uri{
	uri=$1
	dst=$2
	object=$3

	if(~ $object '')
		object=master

	dprint fetch_uri $uri $dst $object

	if(~ $uri '' || ~ $dst '' || ~ $object '')
		exit args

	tar=$object.tar.gz
	switch($website){
	case go.googlesource.com
		# use github since googlesource go archive uses extended attribute to handle long names
		repo=`{basename $uri}
		archive=https://github.com/golang/$repo/archive/$tar
	case *.googlesource.com
		archive=$uri^/+archive/$tar
	case github.com
		archive=$uri^/archive/$tar
	case gopkg.in
		archive=https://github.com/$user/$repo/archive/$tar
	case *
		echo 'fatal: website '''$website''' isn''t supported' >[1=2]
		exit fatal
	}
	file=$tmp/$tar

	rm -f $file
	get -o $file $archive || exit

	if(! test -d $dst)
		mkdir -p $dst

	cd $tmp || exit
	gunzip $file || exit
	rm $file || exit
	tar=`{echo $file | sed 's/\.gz$//'}

	switch($website){
	case github.com go.googlesource.com gopkg.in
		cd $tmp || exit
		tar xf $tar || exit
		cd $pwd || exit

		tmpdst=$tmp/$repo-$object^*

		dircp $tmpdst $dst || exit
		rm -rf $tmpdst
	case *.googlesource.com
		cd $pwd || exit
		cd $dst || exit
		tar xf $tar || exit
		cd $pwd || exit
	case *
		echo 'fatal: website '''$website''' isn''t supported' >[1=2]
		exit fatal
	}
}

fn pull{
	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case *
			dprint option $opt
		}
	}

	if(! test -d .git || ! test -f .git/config){
		echo 'fatal: Not a git repository.' >[1=2]
		exit fatal
	}

	if(test $#* -eq 0)
		uri=`{cat .git/config | grep 'url ?= ?' | awk -F'=' '{print $2}'}
	if not
		uri=$1

	parse_repository $uri
	cleanup
	dst=`{pwd}
	$fetch_fn $uri $dst master
}

fn checkout{
	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case *
			dprint option $opt
		}
	}

	object=$1

	if(~ $object '')
		object=master

	if(! test -d .git || ! test -f .git/config){
		echo 'fatal: Not a git repository.' >[1=2]
		exit fatal
	}

	uri=`{cat .git/config | grep 'url ?= ?' | awk -F'=' '{print $2}'}
	parse_repository $uri
	cleanup $dir
	dst=`{pwd}

	$fetch_fn $uri $dst $object
}

fn version{
	echo git version 2.2.0
}

# cmd/dist generates the VERSION.cache file this way:
# % git rev-parse --git-dir
# .git
# % git rev-parse --abbrev-ref HEAD
# master
# % git log -n 1 --format='format: +%h %cd' HEAD
#  +c69e686 Tue Mar 8 19:30:38 2016 +0000
# % cat VERSION.cache
# devel +c69e686 Tue Mar 8 19:30:38 2016 +0000

fn log {
	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case -n
			shift
		case --format*
			echo ' '+master `{date}
			shift
		case *
			dprint option $opt
		}
	}
}

fn rev-parse{
	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case --git-dir
			echo .git
			shift
		case --abbrev-ref
			echo master
			shift
		case *
			dprint option $opt
		}
	}
}

fn show-ref{
}

fn submodule{
}

fn status{
	echo On branch master
	echo Your branch is up to date with '''origin/master'''.
	echo nothing to commit, working tree clean
}

fn config{
	while(~ $1 -*){
		opt=$1
		shift
		switch($opt){
		case *
			dprint option $opt
		}
	}

	if(~ $1 remote.origin.url){
		if(test -f .git/config){
			uri=`{cat .git/config | grep 'url ?= ?' | awk -F'=' '{print $2}'}
			echo $uri
		}
	}
}

# /bin/git init --bare
fn init{
	base='.'
	while(~ $1 -*){
		shift
	}
	if(~ $#* 1)
		base=$1
	if(! test -f $base/.git)
		mkdir $base/.git
}

# /bin/git remote add origin -- https://domain.com/repository/package.git
fn remote{
	if(! ~ $#* 4 || ! ~ $1 add || ! ~ $2 origin  || ! ~ $3 --){
		echo 'fatal: ' $* ' isn''t supported' >[1=2]
		exit 'not supported'
	}
	gitconfig $4 ./
}

fn main{
	mkdir $tmp

	dprint $0 $*

	if(~ $#* 0){
		usage
	}

	pwd=`{pwd}

	cmd=$1
	shift

	switch($cmd){
		case init
			init $*
		case clone
			clone $*
		case pull
			pull $*
		case checkout
			checkout $*
		case reset
			checkout $*
		case fetch
			checkout $*
		case version
			version $*
		case log
			log $*
		case rev-parse
			rev-parse $*
		case show-ref
			show-ref $*
		case remote
			remote $*
		case status
			status $*
		case submodule
			submodule $*
		case config
			config $*
		case --*
			shift
		case *
			usage
	}

	rm -r $tmp
}

main $*