#!/bin/rc rfork en tmp=/tmp/git.$pid fn usage{ echo 'usage: git []' >[1=2] exit usage } # supported commands: # git checkout [options] [] # git clone [options] [] # git pull [options] [ # git reset [options] [] 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 * 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 *.googlesource.com archive=$uri^/+archive/$tar case github.com archive=$uri^/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 *.googlesource.com cd $pwd || exit cd $dst || exit tar xf $tar || exit cd $pwd || exit case github.com cd $tmp || exit tar xf $tar || exit cd $pwd || exit tmpdst=$tmp/$repo-$object^* dircp $tmpdst $dst || exit rm -rf $tmpdst 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 remote{ } fn submodule{ } fn main{ mkdir $tmp dprint $0 $* if(~ $#* 0){ usage } pwd=`{pwd} cmd=$1 shift switch($cmd){ 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 submodule submodule case --* shift case * usage } rm -r $tmp } main $*